java中的反射和代理

java宝典 2021-02-23 16:57:20
jdk cglib snake object-proxy


Java反射机制可以动态地获取类的结构,动态地调用对象的方法,是java语言一个动态化的机制。java动态代理可以在不改变被调用对象源码的前提下,在被调用方法前后增加自己的操作,极大地降低了模块之间的耦合性。这些都是java的基础知识,要想成为一名合格的程序猿,必须掌握!

Java反射机制

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

Java反射机制允许程序在运行时判断分析任意一个类的结构,包括成员变量和方法,并调用任意一个对象的方法。Eclipse可以自动弹出对象的方法及属性,就是利用了反射的原理。Java的动态代理就是利用了反射的特性来实现的。

1、获取类Class对象

1) A.class:不会加载类,也不会执行静态代码段;

2) Class.forName("cn.test.reflect.A"):要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段;

3) new A().getClass():通过对象获取class

2、反射创建对象

1) 通过默认构造函数创建对象:Class<?> t = Class.forName("cn.test.reflect.A"); t.newInstance();

2) 通过指定构造函数创建对象:Class<?> t = Class.forName("cn.test.reflect.A"); Constructor<?> cons[] = t.getConstructors(); A a = (A) cons[2].newInstance("aa","bb");

注:

① Class<?>表示任何类型的类;

② newInstance()方法只能调用public的无参构造函数,它和new关键字创建实例的区别:创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类;

3、Class类常用方法

getName():获得类的完整名字;

getSuperclass():获得类的父类;

newInstance() :通过类的不带参数的构造方法创建这个类的一个对象;

getFields() :获得当前类和父类中的public类型的所有属性;

getDeclaredFields() :获得当前类(不包含父类)声明的所有属性,包括private和public;

注:对于某个属性field,设置field.setAccessible(true),即可访问private的属性值,如field.get(obj)

getMethods() :获得前类和父类中public类型的所有方法;

getDeclaredMethods():获得当前类(不包含父类)声明的所有方法,包括private和public;

getMethod(String name, Class[] parameterTypes):获得类的指定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型;

getConstructors() :获得当前类的public类型的构造方法;

getDeclaredConstructors() :获得当前类的public和private类型的构造方法;

getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型;

getInterfaces():获得实现的接口;

getSuperclass() :获得继承的父类;

4、Java反射使用实例

ThirdPartyImage_01ef006a.png ThirdPartyImage_29c8068a.png

package com.hicoor.test.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
class B{
public int b;
public B(){}
}
interface IA{}
class A extends B implements IA{
public A() { }
public A(String str) { }
public A(String str1, String str2) { }
private String str;
public int age;
public int func1(String name) {
System.out.println("hello " + name);
return 8;
}
public void func1(String name1, String name2) {
System.out.println("hello "+name1+","+name2);
}
}
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// 根据字符串获得类
Class<?> demoClass = Class.forName("com.hicoor.test.reflect.A");
// 获得类的完整名字
System.out.println("类名"+demoClass.getName()); // com.hicoor.test.reflect.A
// 获得类加载器,默认sun.misc.Launcher$AppClassLoader
System.out.println("类加载器:"+demoClass.getClassLoader().getClass().getName());
//根据Class的共有无参构造方法创建一个实例
A newAObj = (A)demoClass.newInstance();
// 获得类中声明的属性
Field[] publicFields = demoClass.getFields(); // 获得当前类和父类中的public类型的所有属性,返回:age
Field[] declareFields = demoClass.getDeclaredFields(); // 获得当前类(不包含父类)声明的所有属性,包括private和public,返回:str age
Field specifyField = demoClass.getField("age"); // 根据名称获取指定属性
specifyField.setAccessible(true);
//修改属性
specifyField.set(newAObj, 88);
// 获得类的方法
Method[] publicMethods = demoClass.getMethods(); // 获得前类和父类中public类型的所有方法
Method[] declareMethods = demoClass.getDeclaredMethods(); // 获得当前类(不包含父类)声明的所有方法,包括private和public
Method specifyMethod = demoClass.getDeclaredMethod("func1",new Class<?>[]{java.lang.String.class}); //根据方法名和方法参数类型指定获取一个方法
//反射调用对象的方法
specifyMethod.invoke(newAObj, "hans");
//获得构造函数
Constructor<?>[] publicConstructors = demoClass.getConstructors();
Constructor<?>[] declareConstructors = demoClass.getDeclaredConstructors(); //获得当前类声明的所有private和public构造方法
Constructor<?> constructor = demoClass.getConstructor(new Class<?>[]{java.lang.String.class}); //根据指定类型获得构造方法
A newAobj2 = (A)constructor.newInstance("hello"); //根据指定构造函数创建实例
//获得实现的接口
Class<?>[] interfaces = demoClass.getInterfaces();
//获得继承的父类
Class<?> superclass = demoClass.getSuperclass();
}
//反射获得一个方法的明细定义
private static void getMethodDetail(Method method) {
String methodModifier = Modifier.toString(method.getModifiers()); //方法修饰符
String returnType = method.getReturnType().getName(); //方法返回值
Class<?>[] parameterTypes = method.getParameterTypes(); //方法参数类型
System.out.print(methodModifier+" "+returnType+" "+ method.getName()+"(");
int i=1;
for (Class<?> parameterType : parameterTypes) {
System.out.print(parameterType.getName() + " arg"+(i++));
if(i<=parameterTypes.length){
System.out.print(",");
}
}
System.out.println(") {}");
}
}

JAVA动态代理

假如有这样的需求,要在某些模块方法调用前后加上一些统一的前后处理操作,比如在添加购物车、修改订单等操作前后统一加上登陆验证与日志记录处理,该怎样实现?首先想到最简单的就是直接修改源码,在对应模块的对应方法前后添加操作。如果模块很多,你会发现,修改源码不仅非常麻烦、难以维护,而且会使代码显得十分臃肿。

这时候就轮到动态代理上场了,它可以通过反射在被调用方法前后加上自己的操作,而不需要更改被调用类的源码,大大地降低了模块之间的耦合性,体现了极大的优势。

1、JDK动态代理

JDK动态代理中包含一个类和一个接口,即Proxy类和InvocationHandler接口。

1) InvocationHandler接口:

public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}

参数说明:① Object proxy:指被代理的对象;② Method method:要调用的方法;③ Object[] args:方法调用时所需要的参数;

2) Proxy类: Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

参数说明:① ClassLoader loader:类加载器;② Class<?>[] interfaces:得到全部的接口;③ InvocationHandler h:得到InvocationHandler接口的子类实例 ;

3) 关于类加载器

在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有以下三种类加载器:

① Booststrap ClassLoader:此加载器采用C++编写,通常加载jre/lib/rt.jar,一般开发中是看不到的;

② Extendsion ClassLoader:用来进行扩展类的加载,通常加载jre/lib/ext/*.jar;

③ AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器;

4) JDK动态代理实例:

ThirdPartyImage_e8d3bc62.png ThirdPartyImage_8e9de374.png

package com.hicoor.test.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Animal {
public void makeSound(String name);
}
class Dog implements Animal {
@Override
public void makeSound(String name) {
System.out.println("Hi," + name + ",wang,wang~~");
}
}
class Cat implements Animal {
@Override
public void makeSound(String name) {
System.out.println("Hi," + name + ",miao,miao~~");
}
}
/**
* @author Hans 通用动态代理类,被调用对象方法前后增加特殊操作一样的类可都用此类代理
*/
class AnimalProxy implements InvocationHandler {
// 要代理的对象
private Object target;
/**
* 绑定委托对象并返回一个代理类
*
* @param target
* @return
*/
public Object getInstance(Object target) {
this.target = target;
// 取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
System.out.println("方法调用前操作..");
// 执行被调用方法主体
result = method.invoke(target, args);
System.out.println("方法调用后操作..");
return result;
}
}
public class DynamicProxyJDKDemo {
public static void main(String[] args) {
AnimalProxy proxy = new AnimalProxy();
Animal dogProxy = (Animal) proxy.getInstance(new Dog());
dogProxy.makeSound("Tom");
}
}

2、Cglib动态代理

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

使用cglib代理需下载cglib.jar文件,下载地址:[http://www.java2s.com/Code/Jar/c/Downloadcloudcglibjar.htm][http_www.java2s.com_Code_Jar_c_Downloadcloudcglibjar.htm]

Cglib动态代理实例:

ThirdPartyImage_8bafaa8c.png ThirdPartyImage_4190cfe3.png

package com.hicoor.test.dynamicProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
class Snake{
public void makeSound(String name) {
System.out.println("Hi," + name + ",si,si~~");
}
}
class AnimalProxyCglib implements MethodInterceptor {
// 要代理的对象
private Object target;
/**
* 创建代理对象
*
* @param target
* @return
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
Object result = null;
System.out.println("方法调用前操作..");
// 执行被调用方法主体
result = proxy.invokeSuper(obj, args);
System.out.println("方法调用后操作..");
return result;
}
}
public class DynamicProxyCglibDemo {
public static void main(String[] args) {
AnimalProxyCglib proxy = new AnimalProxyCglib();
Snake dogProxy = (Snake) proxy.getInstance(new Snake());
dogProxy.makeSound("Tom");
}
}

关注公众号:java宝典 a

版权声明
本文为[java宝典]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/4569968/blog/4960505

  1. k8s-prometheus
  2. Linux Disk Command
  3. Linux FS
  4. 使用docker-compose &WordPress建站
  5. Linux Command
  6. This time, thoroughly grasp the depth of JavaScript copy
  7. Linux Disk Command
  8. Linux FS
  9. Using docker compose & WordPress to build a website
  10. Linux Command
  11. 摊牌了,我 HTTP 功底贼好!
  12. shiro 报 Submitted credentials for token
  13. It's a showdown. I'm good at it!
  14. Shiro submitted credentials for token
  15. Linux Stress test
  16. Linux Root Disk Extension
  17. Linux Stress test
  18. Linux Root Disk Extension
  19. Redis高级客户端Lettuce详解
  20. springboot学习-综合运用(一)
  21. 忘记云服务器上MySQL数据库的root密码时如何重置密码?
  22. Detailed explanation of lettuce, an advanced client of redis
  23. Springboot learning integrated application (1)
  24. Linux File Recover
  25. Linux-Security
  26. How to reset the password when you forget the root password of MySQL database on the cloud server?
  27. Linux File Recover
  28. Linux-Security
  29. LiteOS:盘点那些重要的数据结构
  30. Linux Memory
  31. Liteos: inventory those important data structures
  32. Linux Memory
  33. 手把手教你使用IDEA2020创建SpringBoot项目
  34. Hand in hand to teach you how to create a springboot project with idea2020
  35. spring boot 整合swagger2生成API文档
  36. Spring boot integrates swagger2 to generate API documents
  37. linux操作系统重启后 解决nginx的pid消失问题
  38. Solve the problem of nginx PID disappearing after Linux operating system restart
  39. JAVA版本号含义
  40. The meaning of java version number
  41. 开源办公开发平台丨Mysql5.7两套四节点主从结构环境搭建教程(二)
  42. 开源办公开发平台丨Mysql5.7两套四节点主从结构环境搭建教程(一)
  43. Open source office development platform mysql5.7 two sets of four node master-slave structure environment building tutorial (2)
  44. HTTP的“无状态”和REST的“状态转换”
  45. Open source office development platform mysql5.7 two sets of four node master-slave structure environment building tutorial (1)
  46. 【大数据哔哔集20210128】使用Hive计算环比和同比
  47. 【大数据哔哔集20210125】Kafka将逐步弃用对zookeeper的依赖
  48. 【大数据哔哔集20210124】有人问我Kafka Leader选举?我真没慌
  49. 【大数据哔哔集20210123】别问,问就是Kafka高可靠
  50. Spring 事务、异步和循环依赖有什么关系?
  51. Spring 动态代理时是如何解决循环依赖的?为什么要使用三级缓存?
  52. "Stateless" of HTTP and "state transition" of rest
  53. [big data bibiji 20210128] use hive to calculate month on month and year on year
  54. [big data bibiji 20210125] Kafka will gradually abandon its dependence on zookeeper
  55. [big data beeps 20210124] someone asked me about Kafka leader election? I'm not in a panic
  56. [big data bibiji 20210123] don't ask, ask is Kafka highly reliable
  57. jQuery Gantt Package 在Visual Studio中创建一个新的ASP.NET项目
  58. What is the relationship between spring transactions, asynchrony, and circular dependencies?
  59. How to solve circular dependency in spring dynamic proxy? Why use level 3 caching?
  60. Unicode码的二进制转换(Java)