JAVA-从反射到动态代理

61 阅读4分钟

反射

定义

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

示例

TUser user = new TUser();
user.action("正射");
//构造class
Class clazz = Class.forName("com.tchy.userapi.entity.TUser");
//使用class获取方法
Method method = clazz.getMethod("action", String.class);
method.invoke(clazz.newInstance(), "反射");
//使用class获取类
TUser user1 = (TUser) clazz.getConstructor().newInstance();
user1.action("反射2");

运行结果为:

Connected to the target VM, address: '127.0.0.1:52500', transport: 'socket'
正在做的动作:正射
正在做的动作:反射
正在做的动作:反射2
Disconnected from the target VM, address: '127.0.0.1:52500', transport: 'socket'

Process finished with exit code 0

Class对象

每个类都有一个Class对象,每当编译一个新类就产生一个Class对象(更恰当地说,是被保存在一个同名的.class文件中)。比如创建一个Student类,那么,JVM就会创建一个Student对应Class类的Class对象,该Class对象保存了Student类相关的类型信息。

Class类的对象作用是运行时提供或获得某个对象的类型信息

获取Class对象方法

有三种方式

//使用class.forName方法
Class clazz = Class.forName("com.tchy.userapi.entity.TUser");
//使用类的.class获取
Class clazz = TUser.class;
//使用示例对象的getClass()方法
TUser user1 = new TUser();
Class clazz = user1.getClass();

获取到class对象后可以使用class的方法:

  • 创造对象:
//使用.newInstance()
TUser user1 = (TUser) clazz.newInstance();
//使用.getConstructor().newInstance()
TUser user2 = (TUser) clazz.getConstructor().newInstance();
  • 获取类的构造器:
//获取所有公共的构造方法
clazz.getConstructors();
//获取所有构造方法
clazz.getDeclaredConstructors();
//返回指定参数类型的public的构造器
clazz.getConstructor();
//返回指定参数类型的public的构造器
clazz.getDeclaredConstructor();
  • 获取类的成员变量:
//获取类的public类型的属性
clazz.getFields();
//获取所有类的属性
clazz.getDeclaredFields();
//根据变量名,返回一个具有public类型的属性
clazz.getField("action");
//根据变量名,返回一个属性
clazz.getDeclaredField("action");
  • 获取类的方法:
//获取类的所有public方法
clazz.getMethods();
//获取类的所有方法
clazz.getDeclaredMethods();
//根据变量名获取类的指定方法
clazz.getMethod("action", String.class);

动态代理

静态代理

就是设计模式中的代理模式 代理模式是一种结构性设计模式,其目的是通过创建一个代理类来控制对另一个对象的访问。代理模式涉及到两个主要角色:

  1. 主题接口(Subject Interface): 定义了代理类和真实主题类共同实现的接口,这样代理类就可以替代真实主题类。
  2. 真实主题类(Real Subject Class): 实现了主题接口,是代理类所代表的真实对象。代理类通过调用真实主题类的方法来实现对真实对象的访问。
  3. 代理类(Proxy Class): 实现了主题接口,包含一个指向真实主题类的引用。代理类可以控制客户端对真实主题类的访问,例如在访问真实主题类的方法前后执行一些操作。

动态代理

解决静态代理每个类都需要创建一个代理类的冗余,则产生了动态代理
在运行状态中,需要代理的地方,根据 Subject 和 RealSubject,动态地创建一个 Proxy,用完之后,就会销毁,这样就可以避免了 Proxy 角色的 class 在系统中冗杂的问题了

image.png

Java 动态代理基于经典代理模式,引入了一个 InvocationHandler,InvocationHandler 负责统一管理所有的方法调用。

动态代理步骤:

  1. 获取 RealSubject 上的所有接口列表;

  2. 确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX

  3. 根据需要实现的接口信息,在代码中动态创建 该 Proxy 类的字节码;

  4. 将对应的字节码转换为对应的 class 对象;

  5. 创建 InvocationHandler 实例 handler,用来处理 Proxy 所有方法调用;

  6. Proxy 的 class 对象 以创建的 handler 对象为参数,实例化一个 proxy 对象。

所有动态代理必须要实现 InvocationHandler 这个接口,重写invoke方法; 代码示例:


public class Client {
    public static void main(String[] args) {
        // 我们要代理的真实对象
        Subject realSubject = new RealSubject();

        // 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler handler = new InvocationHandlerDemo(realSubject);

        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
         */
        Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);

        System.out.println(subject.getClass().getName());
        subject.hello("World");
        String result = subject.bye();
        System.out.println("Result is: " + result);
    }
}