JAVA

Java 动态代理

一、概述

1、什么是代理


代理模式是常见的Java设计模式,它的设计是代理类和委托类有相同的接口, 代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及时候处理消息。代理类和委托类之间通常会存在关联关系, 一个代理类的对象和一个委托类的对象关联, 代理类的对象本身并不提供真正的服务,而是通过调用委托类的相应方法来提供特定的服务。

在日常生活中,代理的应用也随处可见,比如中介、代理加盟商等, 本质上都算是一种代理。 以便利店为例,它并不直接生产商品,而是将各个工厂生产的产品聚合在一起, 以方便消费者选择自己需要的东西。在这个过程中, 便利店、工厂、消费者三者之间的关系如下图所示:

在这三者的关系中,便利店就充当了代理的角色, 当然这个例子可能不是一目了然, 用现实生活中的房屋租赁或者法律诉讼来描述应该更为贴切。

正是因为存在便利店这这个代理,工厂不需要将大量的商品逐个寻找消费者并出售给对方, 消费者购买多种商品时,也不需要寻找多个工厂去一一购买, 通过便利店这一代理角色, 极大的简化了工厂和消费者之间的供需关系。

通过便利店的例子可以发现, 代理在某些场景下可以给我们带来很多遍历,将复杂的事情简单化。生活中如此,代码中亦是如此。

2、代理模式


代理模式给某个对象提供一个代理对象, 并由代理对象控制对原对象的引用。当用户不适合或不能直接访问目标对象时,代理对象作为用户和目标对象之间的中介。

代理模式是一种常见的设计模式, 它有以下特点

  • 代理类和委托类之间存在关联关系, 一个代理类和一个委托类关联。
  • 代理类并不提供真正服务, 而是去调用委托类的服务。
  • 用户通过调用代理对象间接的去调用委托对象的服务。
  • 代理类主要负责在委托类被调用的前后做一些预处理的工作,比如打印日志、安全检查等。

代理模式的角色

  • 抽象角色(Subject): 通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  • 真实角色(Real Subject): 实现了抽象主题中的具体业务,是代理对象所代表的真实对象, 是最终要引用的对象。
  • 代理(Proxy): 提供了和真实对象相同的接口,其内部含有对真实对象的引用, 它可以访问、控制或扩展真实对象的功能。
  • 用户(User): 使用代理类来进行一些操作。

代理模式的使用场景

  • 隐藏某个类
  • 扩展某个类的功能

优点

  • 代理模式在客户端与被代理对象中间起到一个中介作用和保护被代理对象的作用
  • 代理对象可以扩展目标对象的功能
  • 代理模式可以将客户端和目标对象隔离,一定程度上降低系统的耦合性

3、几种代理模式


Java中存在2种代理模式:静态代理动态代理, 如下图所示:

图中关于静态代理的具体的使用描述不太准确,静态代理也可以代理多个目标对象, 但是对于代理类的实现成本比较高, 要求代理类实现和多个目标对象相同的接口,造成代理类比较复杂繁琐。

4、静态代理


静态代理时最基础、最标准的代理模式。以租房为例子:

1、创建一个租房接口

/**
 * 租房接口
 */
public interface IRent {
    void Rent();
}

2、创建一个租房实现类

public class LandLord implements IRent {
    @Override
    public void Rent() {
        System.out.println("房屋出租");
    }
}

3、创建一个租房代理类

public class RentProxy implements IRent {

    private IRent rent;

    public RentProxy(IRent rent){
        this.rent = rent;
    }

    @Override
    public void Rent() {
        seeHouse();
        this.rent.Rent();
        fare();
    }

    void seeHouse(){
        System.out.println("带客户去看房");
    }

    void fare(){
        System.out.println("收取中介费");
    }
}

4、创建客户端

public class RentClient {
    public static void main(String[] args) {
        IRent rent = new LandLord();
        IRent proxy = new RentProxy(rent);
        proxy.Rent();
    }
}

5、运行结果

如上例子所示,静态代理的使用方式非常简单, 通过编程中的组合方法可以轻易实现代理类并为被代理对象增强功能。代理模式可以在不修改被代理对象的基础上,通过代理类,进行一些功能的扩展和增强。值得注意的时,代理类和被代理对象应该实现统一接口,或者共同继承某个类。

静态代理需要预先定义好并且和被代理对象绑定,并且实现和被代理对象一样的接口,如果业务非常庞大,就需要定义大量的代理类,并且如果业务增加新的方法,目标对象和代理对象都需要同时维护,维护成本比较高。

比如上面的租房的同时,还有搬家的需求,如果我们同时实现一个搬家的代理,有2种方式:
1)创建新的搬家的代理类,好处是代码简单易懂,耦合性低,缺点是需要创建并维护一个新的代理类。
2)在租房代理类中同时代理搬家的需求,好处是不需要创建维护新的代理类,缺点是该代理类变得比较复杂臃肿,同样维护成本较高。
在业务比较简单的时候,静态代理非常适合作为代理模式的生产实现,但是随着代码量逐渐增长,业务变得复杂, 静态代理的缺点就会凸显出来。

静态代理的特点:

  • 代理类需要代码运行前创建好
  • 代理类需要在是实现时就指定与被代理类相同的接口

总体来说, 静态代理实现比较简单易懂,但是代码耦合性较高, 维护成本高,业务复杂的场景下不适合使用。

5、动态代理


代理类在代码运行时创建的代理称之为动态代理。动态代理中代理类并不是预先在Java代码中定义好的,而是运行时由JVM动态生成,并且可以代理多个目标对象。

5.1、动态代理的原理

动态代理的本质是在字节码的加载过程中生成Proxy字节码,这样就具备动态生成代理类的能力。

类的加载过程

在运行时期按照class文件的组织规范生成新的字节码就可以生成新的Java类。在Java中有多个类库可以实现这些功能, 比如ASM, javaasist等。

5.2、Java中动态代理的实现方式

  • jdk 动态代理:Java在JDK1.3后引入的动态代理机制, 可以在运行期动态的创建代理类, 只支持接口方式, 使用反射实现。
  • cglib 动态代理:cglib是一个强大、高效的字节码生成类库,底层采用了ASM。cglib通过操纵字节码来实现动态代理,不强制代理对象必须实现接口, 支持通过继承代理类的子类的方式来完成代理。
  • javaasist 动态代理:javaasist是一个开源的创建、分析、编辑Java字节码的类库。主要的优点在于简单、快速,使用者不需要了解汇编指令,就可有动态修改类的结构或者动态生成类。javaasist自带动态代理的实现。
  • javaasist 字节码:javaasist支持使用字符串动态拼接Java源代码的方式生成class字节码来实现动态代理。
  • ASM字节码: ASM是一个Java字节码操控框架, 它能够以二进制形式修改已有类或者动态生成类。ASM可以直接产生二进制class文件, 也可以在类被加载如JVM虚拟机之前改变类行为。ASM在创建class字节码的过程中,操纵的是底层JVM的汇编指令,要求使用者对class的结构和JVM汇编指令比较了解。

5.3、实现方式对比

从易用性上来说:

  • 1、JDK代理的实现方式最简单, 不过只能代理接口。
  • 2、其次是CGLIB, 使用也很方便, 不过需要额外引入CGLIB的Jar包。
  • 3、Javaasist需要使用字符串拼接Java源代码, 比较繁琐。
  • 4、ASM需要手工写字节码, 对个人要求比较高。

总体而言ASM和Javaasist性能最好, JDK和CGLIB使用方便, 生产环境下如果要兼顾易用性和性能,推荐使用Javaasist, 其他情况下CGLIB比较合适。

二、动态代理

1、JDK 动态代理


1.1、概述

JDK动态代理是Java JDK自带的一个动态代理实现, 位于java.lang.reflect包下。JDK动态代理主要包含两个重要的类:

  • java.lang.reflect.Proxy: Proxy负责生成动态代理类。Proxy类有一个很重要的方法:Proxy.newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, InvocationHandler h), 它的功能就是根据被代理类和InvocationHandler 接口的实现类动态生成代理类的class字节码, 并返回动态代理类实例。
  • java.lang.reflect.InvocationHandler: InvocationHandler负责具体的代理方法实现。InvocationHandler接口只有一个方法:invoke(Object proxy, Method method, Object[] args), 该方法利用反射来调用被代理类的指定方法并返回结果, 并且在该方法中可以调用代理类的其他方法在调用前后执行一些其他动作来增强目标类的功能, 比如安全检查、打印日志等等。

创建JDK动态代理需要先实现InvocationHandler接口, 并重写其中的invoke方法,具体步骤如下:

  • 创建一个业务接口和业务的实现类。
  • 创建一个类实现InvocationHandler接口并重写invoke方法。
  • 调用Proxy.newProxyInstance方法生成动态代理类的class字节码并返回动态代理类的实例。
  • 客户端调用动态代理类的具体业务方法。
//1、创建IBusinessService接口, 包含若干业务方法someMethod;
//2、创建IBusinessService接口的具体实现类BusinessService;
//3、创建InvocationHandler接口的实现类SomeInvocationHandler, 并重写invoke方法;

//4.1、创建BusinessService实例;
IBusinessService businessService = new BusinessService();

//4.2、获得BusinessService实例的类加载器;
ClassLoader classLoader = businessService.getClass().getClassLoader();

//4.3、获得BusinessService实例实现的接口集合;
Class<?> interfaces = businessService.getClass().getInterfaces();

//4.4、创建InvocationHandler的实例;
SomeInvocationHandler invocationHandler = new SomeInvocationHandler(businessService);

//4.5、调用Proxy类生成BusinessService的代理类实例;
IBusinessService businessProxy = (IBusinessService) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

//4.6、调用代理类的业务方法获取结果。
businessProxy.someMethod();

JDK动态代理要求被代理对象必须实现接口, 不支持继承, 有2点原因:

  • Proxy.newProxyInstance方法生成的动态代理类自身已经继承了java.lang.reflect.Proxy类, 而Java是不支持多继承的。
  • Proxy.newProxyInstance生成动态代理类的时候是根据被代理类实现的接口interfaces来动态生成方法, 如果被代理类不实现任何接口,它自身的方法是无法填充到动态代理类中, 生成的动态代理类就会不包含自身的业务方法,导致无法将动态代理类类型转换为被代理类, 并且生成的动态代理类无法被调用。

1.2、应用举例

以前面租房的场景为例, 使用JDK动态代理来描述它的使用方式:

1.创建一个租房的接口IRent.

public interface IRent {
    void Rent();
}

2.创建租房接口的实现类RentService.

public class RentService implements IRent {
    @Override
    public void Rent() {
        System.out.println("房屋出租");
    }
}

3.创建一个InvocationHandler的实现类LogHandler, 目标是增强被代理类的日志行为,在方法执行前后打印日志.

public class LogHandler implements InvocationHandler {
    private Object target; // 被代理对象

    public LogHandler(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        before(); // 事前执行方法, 比如打印日志, 参数校验
        Object result =  method.invoke(target, objects);
        after(); // 事后执行方法, 比如安全检查
        return result; // 返回执行结果
    }

    void before(){
        System.out.println("开始打印日志");
    }

    void after(){
        System.out.println("完成打印日志");
    }
}

4.创建客户端UserClient, 生成代理对象并输出结果.

public class UserClient {
    public static void main(String[] args) {

        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

        // 被代理对象
        RentService rentService = new RentService();

        // 被代理对象类加载器
        ClassLoader classLoader = rentService.getClass().getClassLoader();

        // 被代理对象接口集合
        Class<?>[] interfaces = rentService.getClass().getInterfaces();

        // 请求处理器, 处理所有的代理对象上的方法调用
        LogHandler logHandler = new LogHandler(rentService);

        // 创建代理对象
        IRent rentProxy = (IRent) Proxy.newProxyInstance(classLoader, interfaces, logHandler);

        // 调用代理的方法
        rentProxy.Rent();
    }
}

5.输出结果如下:

可以看到不仅输出了被代理对象的结果, 还分别在上下文输出了LogHanderbefore()after()的内容。

从上面可以看到, JDK动态代理不需要事先定义好代理类并且和目标类绑定, 而是通过实现一个InvocationHandler的实现类,在它的invoke方法中实现对被代理类的调用,并且增强原有的功能。JDK代理通过调用Proxy.newProxyInstance()方法来动态生成一个代理类,并且不需要我们手动去处理动态代理类和目标类实现相同的接口, 以及在动态代理类中维护接口的业务方法, 如果业务方法有新增、修改、删除, JDK动态代理会自动帮我们实现。

JDK动态代理还有一个好处, 就是对于同一类型的代理需求只需要实现一个InvocationHandler, 该InvocationHandler不需要和被代理接口绑定, 所以动态代理可以绑定多个代理类而不需要重新实现。
举个例子,还是比如我们有一个搬家的需求,我们定义一个搬家的业务接口IMove和实现类MoveService:

// 搬家接口
public interface IMove {
    void move();
}
// 搬家实现类
public class MoveService implements IMove{
    @Override
    public void move() {
        System.out.println("搬家");
    }
}

然后,我们在搬家业务中也需要日志增强的功能, 这个时候我们是可以复用之前实现的日志增强代理LogHandler。看看客户端同时生成租房和搬家的日志增强代理并输出结果:

  public static void main(String[] args) {

        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

        // 被代理对象
        RentService rentService = new RentService();

        // 被代理对象类加载器
        ClassLoader classLoader = rentService.getClass().getClassLoader();

        // 被代理对象接口集合
        Class<?>[] interfaces = rentService.getClass().getInterfaces();

        // 请求处理器, 处理所有的代理对象上的方法调用
        LogHandler logHandler = new LogHandler(rentService);

        // 创建代理对象
        IRent rentProxy = (IRent) Proxy.newProxyInstance(classLoader, interfaces, logHandler);

        // 调用代理的方法
        rentProxy.Rent();

        IMove moveProxy = (IMove) Proxy.newProxyInstance(MoveService.class.getClassLoader(), MoveService.class.getInterfaces(),
                new LogHandler(new MoveService()));
        moveProxy.move();


    }

}

可以看到在UserClient中,我们使用LogHandler同时生成了RentServiceMoveService的动态代理类,它们2个的动态代理实例被调用过程中都输出了LogHandler的日志内容。JDK动态代理的这一特性非常方便我们实现一些通用的功能比如参数检查、安全检查、日志输出等等。

JDK动态代理的使用也很简单明了,基本和JDK静态代理差别不大, 只是将一对一实现的静态代理类替换成了InvocationHandler

其次,JDK动态代理在运行期间还可以将动态生成的代理类字节码保存到本地, 这样我们就可以查看动态生成的代理类源码。保存的方式有2种:

1.设置JVM系统变量jdk.proxy.ProxyGenerator.saveGeneratedFilestrue:

System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

上面是JDK11的设置方式, JDK8的设置方式如下:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

主函数中添加一句:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

现在主函数长这个样子:

public static void main(String[] args) {
    //这句要放在生成代理对象之前,目的是为了生成$Proxy0.class文件
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    //要被代理的对象(目标对象)
    Inter target = new InterImpl();
    //传入目标对象,生成代理对象
    Inter instance = (Inter) new ProxyFactory(target).getProxyInstance();
    System.out.println(instance.getClass());
    instance.test();
}

执行之后,在项目下多了com文件夹,在com\sun\proxy下生成了$Proxy0.class文件,用idea打开即可自动反编译。生成的代理类字节码保存在workplace/project/com/sun/proxy目录下:

2.调用java.lang.reflect.ProxyGeneratorgeneratedProxyClass方法生成字节码并写入本地(JDK11中该类已经变成包类可见, 不能外部使用)。

JDK动态代理生成的class字节码内容如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import org.good.proxy.IRent;

public final class $Proxy0 extends Proxy implements IRent {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void Rent() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("org.good.proxy.IRent").getMethod("Rent");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到生成的代理类命名为$ProxyNumber, 是因为Proxy类中维护了一个全局的AtomicLong(JDK8)计数器。该$Proxy0代理类继承了java.lang.reflect.Proxy类并且实现了被代理类的接口, 这也是JDK动态代理为什么只支持接口代理的一个原因, 因为Java不支持多继承。

同时可以看到$Proxy0代理类中, 将InvocationHandler实例作为唯一的构造器参数输入, 在每个方法的具体实现中都是调用InvocationHandlerinvoke方法来调用具体的业务方法进行请求代理。而InvocationHandlerinvoke方法实际是通过反射来实现的, 它的输入参数分别是动态代理类自身、被代理的方法、方法参数, 返回类型和业务接口的返回类型一致。

    public final void Rent() throws  {
        try {
            // 其中执行方法的语句为:
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

$Proxy0类继承了Proxy,实现了我们自定义的接口Inter,其中super.hProxyh,即InvocationHandler

其中h正是我们调用newProxyInstance函数时传入的第三个参数。

 @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        before(); // 事前执行方法, 比如打印日志, 参数校验
        Object result =  method.invoke(target, objects);
        after(); // 事后执行方法, 比如安全检查
        return result; // 返回执行结果
    }

其次,生成的动态代理类包含多个java.lang.reflect.Method对象:

  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;

这些Method对象中,除过包含业务接口定义的业务方法, 还包含了三个重要方法, 分别是java.lang.Objectequals()hashCode()toString()方法。

总结而言,JDK动态代理的特点如下:

  • 需要实现InvocationHandler接口, 并重写invoke方法。
  • 被代理类需要实现接口, 它不支持继承。
  • JDK 动态代理类不需要事先定义好, 而是在运行期间动态生成。
  • JDK 动态代理不需要实现和被代理类一样的接口, 所以可以绑定多个被代理类。

1.3、实现原理

JDK动态代理的主要实现原理为反射, 它通过反射在运行期间动态生成代理类, 并且通过反射调用被代理类的实际业务方法。

java.lang.reflect.ProxynewProxyInstance方法入手我们看一下JDK动态代理的实现源码(以JDK11为例)。

1.Proxy 的成员:

public class Proxy implements Serializable {
    private static final long serialVersionUID = -2222568056686623797L;
    private static final Class<?>[] constructorParams = new Class[]{InvocationHandler.class};
    private static final ClassLoaderValue<Constructor<?>> proxyCache = new ClassLoaderValue();
    protected InvocationHandler h;
    private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
    private static final String PROXY_PACKAGE_PREFIX = "com.sun.proxy";

    private Proxy() {
    }

    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

   /**
   *
   */
}

Proxy类的主要成员变量或类变量为:

  • Class<?>[] constructorParams: 生成的代理类的构造器参数, 只有一个InvocationHandler, 从生成的$Proxy0 字节码可以看到, 动态生成的代理类的构造器确实只有一个参数就是InvocationHandler
  • ClassLoaderValue<Constructor<?>> proxyCache: Proxy类的本地缓存,Key存储的是被代理类接口的集合, Value存的是生成的动态代理类的构造器实例。
  • InvocationHandler h: Proxy类的唯一成员变量, 作为构造器的参数传入, 不允许为空。

Proxy类的构造函数只有一个InvocationHandler,因为生成的动态代理类会继承Proxy

   protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

2.Proxy.newProxyInstance()方法

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
        Objects.requireNonNull(h);
        Class<?> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass();
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
        return newProxyInstance(caller, cons, h);
    }

newProxyInstance方法的参数为:

  • ClassLoader: 被代理类的类加载器,用来判断访问权限以及生成的代理类的包路径。
  • Class<?> interfaces: 被代理类实现的接口集合, 用来生成代理类中的业务方法。
  • InvocationHandler: 代理业务逻辑的处理handler, 用来作为Proxy的构造器参数并传递给代理类。

newProxyInstance方法的逻辑比较简单:

  • 获取代理类的构造器实例Constructor.
  • 利用构造器实例和InvocationHandler的实例生成代理类实例.

需要注意必须实现InvocationHandler, 而不能传空。

3.Proxy.getProxyConstructor方法

 private static Constructor<?> getProxyConstructor(Class<?> caller, ClassLoader loader, Class<?>... interfaces) {
        if (interfaces.length == 1) {
            Class<?> intf = interfaces[0];
            if (caller != null) {
                checkProxyAccess(caller, loader, intf);
            }

            return (Constructor)proxyCache.sub(intf).computeIfAbsent(loader, (ld, clv) -> {
                return (new Proxy.ProxyBuilder(ld, (Class)clv.key())).build();
            });
        } else {
            Class<?>[] intfsArray = (Class[])interfaces.clone();
            if (caller != null) {
                checkProxyAccess(caller, loader, intfsArray);
            }

            List<Class<?>> intfs = Arrays.asList(intfsArray);
            return (Constructor)proxyCache.sub(intfs).computeIfAbsent(loader, (ld, clv) -> {
                return (new Proxy.ProxyBuilder(ld, (List)clv.key())).build();
            });
        }
    }

该方法主要做了两件事:

  • proxyCache中获取被代理类的动态代理类构造器, 键值是由被代理类实现的接口集合生成的AbstractClassLoaderValue.SubK实例。
  • 如果从缓存中获取不到, 则通过Proxy.ProxyBuilder动态创建动态代理类的class字节码并生成构造器。

4.Proxy.ProxyBuilder类成员:

  private static final class ProxyBuilder {
        private static final Unsafe UNSAFE = Unsafe.getUnsafe();
        private static final String proxyClassNamePrefix = "$Proxy";
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
        private static final ClassLoaderValue<Boolean> reverseProxyCache = new ClassLoaderValue();
        private static final String DEBUG = GetPropertyAction.privilegedGetProperty("jdk.proxy.debug", "");
        private final List<Class<?>> interfaces;
        private final Module module;
        private static final ClassLoaderValue<Module> dynProxyModules = new ClassLoaderValue();
        private static final AtomicInteger counter = new AtomicInteger();

        ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) {
            if (!VM.isModuleSystemInited()) {
                throw new InternalError("Proxy is not supported until module system is fully initialized");
            } else if (interfaces.size() > 65535) {
                throw new IllegalArgumentException("interface limit exceeded: " + interfaces.size());
            } else {
                Set<Class<?>> refTypes = referencedTypes(loader, interfaces);
                validateProxyInterfaces(loader, interfaces, refTypes);
                this.interfaces = interfaces;
                this.module = mapToModule(loader, interfaces, refTypes);

                assert Proxy.getLoader(this.module) == loader;

            }
          }

          ProxyBuilder(ClassLoader loader, Class<?> intf) {
            this(loader, Collections.singletonList(intf));
          }

}

比较重要的成员变量:

  • proxyClassNamePrefix: 生成动态代理类类名的前缀。
  • nextUniqueNumvber: 生成动态代理类类名的后缀。
    比如$Proxy0$Proxy1, 根据运行时Proxy动态创建的代理类数量决定。
  • ClassLoader: 类加载器, 用来判断包路径和访问权限, 作为构造器参数传入。
  • List<Class<?>> interfaces: 被代理类的接口集合,作为构造器参数传入。

5. Proxy.ProxyBuilder.build方法:

    Constructor<?> build() {
            Class proxyClass = defineProxyClass(this.module, this.interfaces);

            final Constructor cons;
            try {
                cons = proxyClass.getConstructor(Proxy.constructorParams);
            } catch (NoSuchMethodException var4) {
                throw new InternalError(var4.toString(), var4);
            }

            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
            return cons;
        }

build方法主要做2件事情:

  • 生成动态代理类的class字节码。
  • 根据class字节码生成动态代理类的构造器实例。

6.Proxy.ProxyBuilder.defineProxyClass方法:

private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
           String proxyPkg = null;
           int accessFlags = 17;
           Iterator var4 = interfaces.iterator();

           while(var4.hasNext()) {
               Class<?> intf = (Class)var4.next();
               int flags = intf.getModifiers();
               if (!Modifier.isPublic(flags)) {
                   accessFlags = 16;
                   String pkg = intf.getPackageName();
                   if (proxyPkg == null) {
                       proxyPkg = pkg;
                   } else if (!pkg.equals(proxyPkg)) {
                       throw new IllegalArgumentException("non-public interfaces from different packages");
                   }
               }
           }

           if (proxyPkg == null) {
               proxyPkg = m.isNamed() ? "com.sun.proxy." + m.getName() : "com.sun.proxy";
           } else if (proxyPkg.isEmpty() && m.isNamed()) {
               throw new IllegalArgumentException("Unnamed package cannot be added to " + m);
           }

           if (m.isNamed() && !m.getDescriptor().packages().contains(proxyPkg)) {
               throw new InternalError(proxyPkg + " not exist in " + m.getName());
           } else {
               long num = nextUniqueNumber.getAndIncrement();
               String proxyName = proxyPkg.isEmpty() ? "$Proxy" + num : proxyPkg + "." + "$Proxy" + num;
               ClassLoader loader = Proxy.getLoader(m);
               trace(proxyName, m, loader, interfaces);
               byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, (Class[])interfaces.toArray(Proxy.EMPTY_CLASS_ARRAY), accessFlags);

               try {
                   Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile, 0, proxyClassFile.length, loader, (ProtectionDomain)null);
                   reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
                   return pc;
               } catch (ClassFormatError var10) {
                   throw new IllegalArgumentException(var10.toString());
               }
           }
       }

defineProxyClass方法的执行过程如下:

  • 生成代理类包路径:判断被代理类实现的接口中, 是否存在非public修饰的接口, 如果有则设置代理类的包路径为非public接口的包路径, 因为如果是非public修饰的只允许包内可访问, 并且如果存在多个非public接口,还需要判断它们是否位于同一个包下面, 否则会出现访问权限问题。如果包路径为空,则设置包路径为默认的com.sun.proxy
  • 生成代理类类名:根据nextUniqueNumber获取下一位数值并拼接类名。
String proxyName = proxyPkg.isEmpty() ? "$Proxy" + num : proxyPkg + "." + "$Proxy" + num;
  • 生成代理类class字节码: 调用ProxyGenerator.generateProxyClass方法生成代理类的字节码byte[] proxyClassFile。参数是类名和接口Class 集合。
  • 生成代理类java.lang.Class对象: 调用系统方法使用class字节码生成对应的java.lang.Class对象。

defineProxyClass方法的返回为java.lang.Class, 是动态生成的代理类Class实例。

7.ProxyGenerator类:

class ProxyGenerator {
    private static final int CLASSFILE_MAJOR_VERSION = 49;
    private static final int CLASSFILE_MINOR_VERSION = 0;
    private static final int CONSTANT_UTF8 = 1;
    private static final int CONSTANT_UNICODE = 2;
    private static final int CONSTANT_INTEGER = 3;
    private static final int CONSTANT_FLOAT = 4;
    private static final int CONSTANT_LONG = 5;
    private static final int CONSTANT_DOUBLE = 6;
    private static final int CONSTANT_CLASS = 7;
    private static final int CONSTANT_STRING = 8;
    private static final int CONSTANT_FIELD = 9;
    private static final int CONSTANT_METHOD = 10;
    private static final int CONSTANT_INTERFACEMETHOD = 11;
    private static final int CONSTANT_NAMEANDTYPE = 12;
    private static final int ACC_PUBLIC = 1;
    private static final int ACC_PRIVATE = 2;
    private static final int ACC_STATIC = 8;
    private static final int ACC_FINAL = 16;
    private static final int ACC_SUPER = 32;
    private static final int opc_aconst_null = 1;
    private static final int opc_iconst_0 = 3;
    private static final int opc_bipush = 16;
    private static final int opc_sipush = 17;
    private static final int opc_ldc = 18;
    private static final int opc_ldc_w = 19;
    private static final int opc_iload = 21;
    private static final int opc_lload = 22;
    private static final int opc_fload = 23;
    private static final int opc_dload = 24;
    private static final int opc_aload = 25;
    private static final int opc_iload_0 = 26;
    private static final int opc_lload_0 = 30;
    private static final int opc_fload_0 = 34;
    private static final int opc_dload_0 = 38;
    private static final int opc_aload_0 = 42;
    private static final int opc_astore = 58;
    private static final int opc_astore_0 = 75;
    private static final int opc_aastore = 83;
    private static final int opc_pop = 87;
    private static final int opc_dup = 89;
    private static final int opc_ireturn = 172;
    private static final int opc_lreturn = 173;
    private static final int opc_freturn = 174;
    private static final int opc_dreturn = 175;
    private static final int opc_areturn = 176;
    private static final int opc_return = 177;
    private static final int opc_getstatic = 178;
    private static final int opc_putstatic = 179;
    private static final int opc_getfield = 180;
    private static final int opc_invokevirtual = 182;
    private static final int opc_invokespecial = 183;
    private static final int opc_invokestatic = 184;
    private static final int opc_invokeinterface = 185;
    private static final int opc_new = 187;
    private static final int opc_anewarray = 189;
    private static final int opc_athrow = 191;
    private static final int opc_checkcast = 192;
    private static final int opc_wide = 196;
    private static final String superclassName = "java/lang/reflect/Proxy";
    private static final String handlerFieldName = "h";
    private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("jdk.proxy.ProxyGenerator.saveGeneratedFiles"));
    private static Method hashCodeMethod;
    private static Method equalsMethod;
    private static Method toStringMethod;
    private String className;
    private Class<?>[] interfaces;
    private int accessFlags;
    private ProxyGenerator.ConstantPool cp = new ProxyGenerator.ConstantPool();
    private List<ProxyGenerator.FieldInfo> fields = new ArrayList();
    private List<ProxyGenerator.MethodInfo> methods = new ArrayList();
    private Map<String, List<ProxyGenerator.ProxyMethod>> proxyMethods = new HashMap();
    private int proxyMethodCount = 0;

    private ProxyGenerator(String className, Class<?>[] interfaces, int accessFlags) {
        this.className = className;
        this.interfaces = interfaces;
        this.accessFlags = accessFlags;
    }

}

可以看到ProxyGenerator类的成员除过包含类名className和类的接口集合interfaces外, 还包含3个Method对象:

 private static Method hashCodeMethod;
 private static Method equalsMethod;
 private static Method toStringMethod;

这3个方法是多有JDK 动态代理生成的代理类都会包含的方法。

8.ProxyGenerator.generateProxyClass方法:

 static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) {
        ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
        final byte[] classFile = gen.generateClassFile();
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int i = name.lastIndexOf(46);
                        Path path;
                        if (i > 0) {
                            Path dir = Path.of(name.substring(0, i).replace('.', File.separatorChar));
                            Files.createDirectories(dir);
                            path = dir.resolve(name.substring(i + 1, name.length()) + ".class");
                        } else {
                            path = Path.of(name + ".class");
                        }

                        Files.write(path, classFile, new OpenOption[0]);
                        return null;
                    } catch (IOException var4) {
                        throw new InternalError("I/O exception saving generated file: " + var4);
                    }
                }
            });
        }

        return classFile;
    }

该方法主要做2件事:

  • 调用generateClassFile方法生成class字节码。
  • 判断是否需要将class字节码写入磁盘。

9.ProxyGenerator.generateClassFile方法:

  private byte[] generateClassFile() {
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class intf;
        for(var3 = 0; var3 < var2; ++var3) {
            intf = var1[var3];
            Method[] var5 = intf.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method m = var5[var7];
                if (!Modifier.isStatic(m.getModifiers())) {
                    this.addProxyMethod(m, intf);
                }
            }
        }

        Iterator var11 = this.proxyMethods.values().iterator();

        List sigmethods;
        while(var11.hasNext()) {
            sigmethods = (List)var11.next();
            checkReturnTypes(sigmethods);
        }

        Iterator var15;
        try {
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();

            while(var11.hasNext()) {
                sigmethods = (List)var11.next();
                var15 = sigmethods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod pm = (ProxyGenerator.ProxyMethod)var15.next();
                    this.fields.add(new ProxyGenerator.FieldInfo(pm.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    this.methods.add(pm.generateMethod());
                }
            }

            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }

        if (this.methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        } else if (this.fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            var1 = this.interfaces;
            var2 = var1.length;

            for(var3 = 0; var3 < var2; ++var3) {
                intf = var1[var3];
                this.cp.getClass(dotToSlash(intf.getName()));
            }

            this.cp.setReadOnly();
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            DataOutputStream dout = new DataOutputStream(bout);

            try {
                dout.writeInt(-889275714);
                dout.writeShort(0);
                dout.writeShort(49);
                this.cp.write(dout);
                dout.writeShort(this.accessFlags);
                dout.writeShort(this.cp.getClass(dotToSlash(this.className)));
                dout.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                dout.writeShort(this.interfaces.length);
                Class[] var17 = this.interfaces;
                int var18 = var17.length;

                for(int var19 = 0; var19 < var18; ++var19) {
                    Class<?> intf = var17[var19];
                    dout.writeShort(this.cp.getClass(dotToSlash(intf.getName())));
                }

                dout.writeShort(this.fields.size());
                var15 = this.fields.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo f = (ProxyGenerator.FieldInfo)var15.next();
                    f.write(dout);
                }

                dout.writeShort(this.methods.size());
                var15 = this.methods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo m = (ProxyGenerator.MethodInfo)var15.next();
                    m.write(dout);
                }

                dout.writeShort(0);
                return bout.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }

该方法的主要作用是按照class字节码的组织结构构建class字节流,比较复杂专业, 翻看源码大致流程为:

  • 构造Method成员, 这里包含了hashCodeequalstoString3个方法等。
  • 构造Field成员。
  • 构造字节码输出流DataOutputStream
  • stream中先后写入魔数、代理类访问控制符、代理类、java.lang.Proxy、接口、成员变量、方法变量等等。

10.InvocationHandler接口:

public interface InvocationHandler {
    Object invoke(Object var1, Method var2, Object[] var3) throws Throwable;
}

InvocationHandler 接口比较简单, 只要一个方法invoke,由业务方自行实现业务逻辑。实现的方式是通过Method反射来实现真实的业务方法调用。

综上可以看到,Proxy类的主要作用是动态生成代理类 Class实例,真实的业务代码注入都在InvocationHandler中实现, 业务方需要重写invoke方法完成真正的业务调用,并实现功能增强。JDK总台代理主要是通过反射的方式来动态生成字节码从而实现动态代理。

1.4、存在的问题

JDK动态代理虽然实现很简单, 但是要求被代理类必须实现方法, 而不支持继承,因为生成的代理类已经继承了java.lang.reflect.Proxy类, Java不支持多继承。

如果我们定义一个被代理类,该类不实现任何接口会怎样?下面我们做一个实验。

1.定义一个 RunService不实现任何接口。

public class RunService {
    public void run(){
        System.out.println("跑步");
    }
}

2.InvocationHandler继续使用上面的LogHandler

public class LogHandler implements InvocationHandler {
    private Object target; // 被代理对象

    public LogHandler(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        before(); // 事前执行方法, 比如打印日志, 参数校验
        Object result =  method.invoke(target, objects);
        after(); // 事后执行方法, 比如安全检查
        return result; // 返回执行结果
    }

    void before(){
        System.out.println("开始打印日志");
    }

    void after(){
        System.out.println("完成打印日志");
    }
}

3.在Client端通过Proxy.newInstance获取代理类实例,并调用run方法。

public class UserClient {
    public static void main(String[] args) {

       System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

        System.out.println("开始测试...");
        RunService runService = new RunService();
        ClassLoader classLoader = runService.getClass().getClassLoader();
        Class<?>[] interfaces = runService.getClass().getInterfaces();
        LogHandler logHandler = new LogHandler(runService);

        RunService runProxy = (RunService)Proxy.newProxyInstance(classLoader, interfaces, logHandler);
        runProxy.run();
        System.out.println("结束测试...");

    }
}

此处IDEA中代码并没有报错, 编译也是可以通过的。

4.执行UserClient看看是否可以执行成功。

可以看到第40行报错了, 错误内容为生成的动态代理类$Proxy0无法转换为RunService类型。经过Debug Proxy.newProxyInstance方法确实生成了代理类对象, 那么是为什么呢,我们来看看生成的class文件:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy {
    private static Method m1;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到由于RunService没有实现任何接口, 所以生成的代理类也没有实现任任何接口, 并且生成的代理类只有3个默认的方法, 并不包含期待的run方法。由于$Proxy0类和RunService没有实现共同的接口也没有继承共同的父类, 所以它们无法相互转换, 就会出现上图中转换失败, 由于代理类是运行时创建,所以代理编译器并不会报出错误。

另外,ProxyGenerator.generateProxyClass生成代理类字节码的时候, 是根据被代理类继承的接口来构造Method, 所以代理类自身的方法并不会被添加到代理类中, 如果被代理类不实现任何方法, 虽然可以成功生成代理类, 但是生成的代理类只包含3个默认方法。

综上上述, JDK动态代理必须通过接口实现, 主要原因如下:

  • 生成的代理类自身已经继承java.lang.reflect.Proxy类,由于Java不支持多继承, 所以被代理类不能继承任何类, 负责无法实现。
  • 如果被代理类不实现任何接口, 生成的代理类和被代理类无共同接口或父类,无法相互转换,且不包含被代理类自有的方法。
  • 如果代理类自身已经继承了其他父类, 也可以成功生成代理类,但是生成的代理类和不实现任何接口的效果一样。

1.5、总结

JDK 动态代理的实现原理是通过反射 来动态创建或修改字节码来生成动态代理类。它包含2个核心类库:

  • java.lang.reflect.InvocationHandler: 负责具体的业务代码注入。
  • java.lang.reflect.Proxy: 负责动态生成代理类。

JDK 动态代理的主要优点为:

  • 使用简单、维护成本低。
  • Java原生支持,不需要任何依赖。
  • 解决了静态代理存在的多数问题。

主要缺点为:

  • 由于使用反射,性能会比较差。
  • 只支持接口实现,不支持继承, 不满足所有业务场景。

2、 CGLIB


2.1、概述

1、什么是CGLIB?

CGLIB是一个强大的、高性能的代码生成库。它可以在运行期扩展Java类和接口,其被广泛应用于AOP框架中(Spring、dynaop)中, 用以提供方法拦截。Hibernate作为一个比较受欢迎的ORM框架, 用它来实现PO字节码的动态生成。 CGLIBJDK动态代理更强的地方在于它不仅可以接管Java接口, 还可以接管普通类的方法。

CGLIB的底层原理是使用的ASM来操纵字节码。它作为一个开源项目, 代码被托管在github上, 项目地址是:https://github.com/cglib/cglib它通过操纵字节码生成被代理类的一个或多个子类来实现对被代理类的代理

CGLIB和一些常见框架和语言(主要是Java)的关系如下:

  • 最底层的是字节码, 字节码是Java为了保证”一次编译、到处运行“而产生的一种虚拟指令格式, 例如iload_0, iconst_1等等。
  • 字节码上面一层的是ASMASM是Java中可以直接操纵字节码的一个类库, 使用它需要对Java字节码、class组织结构比较熟悉。
  • 位于ASM之上的是CGLIBGroovy等框架和语言,它们通过ASM框架动态生成字节码来变相执行Java程序。这也说明Java并不是唯一可被JVM执行的语言, 只要可以生成字节码,JVM并不关心字节码的来源
  • 位于CGLIB等框架之上的就是SpringHibernate等这些框架了。

2、为什么使用CGLIB?

CGLIB代理主要通过对字节码操作, 为对象引入不同的子类,以控制对象的访问。CGLIB主要解决JDK动态代理要求被代理类必须继承接口的缺点,它可以对非继承自接口的类进行代理, 同时CGLIB的功能更加强大, 支持更多的定制化功能。

3、如何获得CGLIB?
CGLIB的源码维护在github上:https://github.com/cglib/cglib。开发的时候需要引入cglib的jar包, 可以通过maven来引入:

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>cglib-version</version>
</dependency>

4、CGLIB的源码结构
cglib-3.3.0为例, 它主要包含以下几个模块:

  • net.sf.cglib.beans: JavaBean的工具类。
  • net.sf.cglib.core:底层字节码处理类, 大部分和ASM有关系。
  • net.sf.cglib.proxy:实现创建代理和方法拦截器的类。
  • net.sf.cglib.reflect:实现Fast反射和C#风格的类。
  • net.sf.cglib.transform:编译器或运行期类和类文件的转换工具。
  • net.sf.cglib.util: 其他工具类。

CGLIB的底层主要是通过ASM来操纵字节码实现类的动态生成。对于创建动态代理, 大部分情况下, 只需要使用net.sf.cglib.proxy包的一部分API即可。

CGLIB的动态关联图如下:

可以看到, 它核心的API有两个:

  • net.sf.cglib.Enhancer: 代码增强器, 用来动态生成目标类的代理类进行代理。
  • net.sf.cglib.Callback: 目标类被调用时, 被拦截后调用的回调, 用来实现具体的回调逻辑, 增强业务代码, 比如打印日志、安全检查等等。

5、CGLIB的重要API

  • net.sf.cglib.proxy.Enhancer: CGLIB中的字节码增强器, 类似于JDK里的java.lang.reflect.Proxy, 它既可以代理接口, 也可以代理普通的Java类, 它通过创建被代理类的子类并拦截所有的方法调用(包括Object#toString()Object#hashCode), 但是它不能拦截final修饰的方法(如Object#getClass()), 也不能代理final修饰的类。Enhancer类包含几个核心的同名重载方法create来创建代理类, 调用create方法之前需要预先设置被代理类setSuperClass、设置代理回调setCallbacksetCallbacks等等。
  • net.sf.cglib.proxy.Callback: CGLIB中的回调, 作用类似于JDK里的java.lang.reflect.InvocationHandler, 负责业务方法的实现和增强。 它的回调时机是被代理类的方法被调用的时候, 即被代理类的方法被调用时, Callback的实现逻辑就会被调用。可以通过Enhancer#setCallback()Enhancer#setCallbacks()设置Callback, 若设置了多个Callback, 则会按照设置的顺序进行回调。它只是一个标识接口, CGLIB提供了多个它的子类。
  • net.sf.cglib.proxy.CallbackFilter: CallbackFilter可以实现不同的方法使用不同的回调方法。它的int accept(Method method)方法, 根据不同的method返回不同的值i,这个值时回调集合callbacks中的顺序, 就是调用了callbacks[i]
  • net.sf.cglib.proxy.Dispatcher: Callback的常用子类之一, 主要应用在懒加载场景下, 和LazyLoader不同的是每次调用代理方法时都会调用到loadObject方法加载被代理对象。
  • net.sf.cglib.proxy.FixedValue: Callback的常用子类之一, 强制一个特定方法返回固定值, 特定场景下非常有用且性能高。
  • net.sf.cglib.proxy.LazyLoader: Callback的常用子类之一, 主要应用在懒加载场景下, 它有一个Object loadObject()方法,用来被代理对象懒加载时初始化该对象。 它的特点是当被声明为懒加载的对象已经被加载完成后, 下次调用时, 就会直接使用该对象, 不会再次调用loadObject()方法进行加载。
  • net.sf.cglib.proxy.MethodInterceptor:Callback的最常用子类, 是最常用的回调类型。它被设置为方法回调时, 当调用代理的方法时, 首先会调用它的intercept方法,然后调用被代理对象的目标方法, 这个过程中在调用前后可以做一些业务增强的功能。intercept方法有4个参数, 分别是代理对象Object、被代理的方法Method、被代理方法的参数Object[] objects、被代理方法的代理MethodProxy
  • net.sf.cglib.proxy.ProxyRefDispatcher: Callback的子类之一, 和Dispatcher相同, 不同的是它的loadObject方法支持传入代理对象。
  • net.sf.cglib.proxy.NoOp: Callback的子类之一, 它代表什么都不做, 直接调用被代理类的方法。
  • net.sf.cglib.proxy.InterfaceMaker: 顾名思义, 它是一个接口生成器, 它可以根据指定的类动态生成一个接口, 该接口包含指定类定义的所有方法。
  • net.sf.cglib.proxy.InvocationHandler: CGLIB中参考JDK动态代理实现的InvocationHandler, 可以和net.sf.cglib.proxy.Proxy配合替换JDK的java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler, 它同样也定义了一个invoke方法, 供按照JDK动态代理的方式实现业务代码的增强修改。
  • net.sf.cglib.proxy:Proxy:CGLIB中参考JDK动态代理实现的代理生成类, 可以和CGLIB自带的InvocationHandler配合按照JDK动态代理规范实现动态代理, 它和java.lang.reflect.Proxy一样支持newProxyInstance方法动态生成代理类。它的内部是调用Enhancer动态生成代理类, 并且设置了一个默认的CallbackFilter, 当拦截到Object#hashCodeObject#equalsObject#toString3个方法时, 调用NoOp回调, 啥都不做, 直接调用父类的方法, 当拦截到其他方式时, 调用用户重写的InvocationHandler来实现回调。
  • net.sf.cglib.proxy.CallbackGenerator: Callback的处理接口, 它是一个接口, 包含2个重要的方法generategenerateStatic, 主要用来实现覆盖父类的方法, 每一个Callback子接口都有一个对应的CallbackGenerator子类。
  • net.sf.cglib.proxy.Factory: CGLIB实现代理所必要实现的接口,用于提供一些工具方法。

6、CGLIB的使用过程

CGLIB的使用, 需要先实现一个Callback回调, 一般常用的是MethodInterceptor子类,它可以满足大多数的动态代理需求, 具体步骤如下:

  • 1.创建一个被代理类。
  • 2.创建一个类实现CallbackCallback的子接口并重写业务织入的逻辑, 也可以不实现, 使用默认的NoOp即可, 功能和没使用动态代理一样, 直接调用被代理类的方法。
  • 3.创建一个Enhancer字节码增强器对象, 为该对象设置被代理类(setSuperClass)、回调setCallback、回调顺序setCallbackFilter等等属性。
  • 4.调用Enhancer实例的create方法, 动态生成一个被代理类子类的字节码并返回实例。
  • 5.客户端调用动态代理类的具体业务方法, 注意这里调用的是CGLIB为被代理类生成的一个子类的实例, 而不是被代理类的实例, CGLIB生成的动态代理类都会继承被代理类。
//1. 创建被代理类Target

//2. 实现回调Callback

//3. 初始化Enhancer
Enhancer enhancer = new Enhancer();
// 设置被代理类
enhancer.setSuperClass(Target.class);
// 设置回调
enhancer.setCallBack(new Callback());
enhancer.setCallbacks({newCallback1(), new Callback2()});
// 设置回调顺序,可以忽略,
// 忽略的话就会对目标类所有的方法执行相同的回调。
enhancer.setCallbackFilter(new CallbackFilter());

//4. 获取代理对象
Target targetProxy = (Target)enhancer.create();

// 5. 客户端调用
targetProxy.method();

转载:https://www.jianshu.com/p/dac09ec2940b