使用JDK创建动态代理有一个限制, 即它只能为接口创建代理实例. 对于没有定义接口的业务方法的类, 使用CDGlib 进行动态代理.
CGLib是一个强大的, 高性能的代码生成库. 被广泛应用于 AOP 框架. 用以提供方法拦截操作.
CGLib采用底层的字节码技术, 可以为一个类创建子类, 在子类中采用方法拦截的技术拦截所有父类方法的调用, 并织入横切逻辑.
CGLib 定义的 intercept() 接口方法, 拦截所有目标类方法的调用. 其中 o 代表目标类的实例, method 为目标类方法的反射; args为方法的动态入参, proxy为代理类实例.
代理对象的生成由 Enhancer 类实现. Enhancer是CGLib的字节码增强器. 可以很方便的对类进行扩展.
Enhancer 创建代理对象的大概步骤如下:
1. 生成代理类 Class 二进制字节码.
2.通过 Class.forname() 加载字节码文件, 生成 Class 对象.
3.通过反射机制获得实例构造, 并创建代理类对象.
通过反编译可以查看代理类的实现, 如下: (此处借鉴他人博客内容)
1. 生成的代理类 UserService$$EnhancerByCGLIB$$394dddeb 继承了被代理类.
如果委托类被 final 修饰, 那么它不可被代理; 如果委托类中存在被 final 修饰的方法, 那么该方法也不可被代理.
2. 代理类为委托方法生成两个方法, 以上述代码中的add()为例, 一个是重写的add() (代理类重写了委托类中的方法), 另一个是 CGLIB$add$0 方法. 这个方法直接调用父类的 add()方法.
3. 当执行代理对象的 add() 方法时, 首先判断一下是否存在实现了 MethodIntercetpor 接口的对象 cglib$CALLBACK_0, 如果存在则调用 MethodInterceptor 对象的 intercept() 方法.
代理类为每个委托方法都生成两个方法, 如上代码所示.
4.每个被代理的方法都应有一个 MethodProxy 对象, methodProxy.invokeSuper() 方法最终调用委托类的方法.
三. 调用委托方法是通过代理方法的 MethodProxy 对象调用 invokeSuper() 方法来执行的.
Method 类中的 invokeSuper() 方法.
现在还看不出来 invokeSuper() 方法如何执行委托类中方法. 在 CGLib 中, 对委托类方法的调用不是通过反射机制实现的, 而是直接对方法进行调用, 通过内部类 FastClassInfo 来调用委托类中的方法.
FastClassInfo 内部类的机制:
以上述代码 add() 方法的 MethodProxy 为例, f1指向委托类对象, f2指向代理类对象, 下标 i1 和 i2 分别是方法 add() 和 CGLIB$add$0 在对象中索引的位置.
FastClassInfo 实际上对 Class 对象进行处理, 提出下标概念 index, 通过索引保存方法的引用信息, 将原先的反射调用转换为直接调用. 所以是直接调用了代理类的 CGLIB$add$0 方法, CGLIB$add$0 调用了委托类中的 add() 方法. FastClass 对对象进行特殊处理, 用数组保存 method 的引用, getIndex()方法通过方法签名来获取方法在存储了 Class 信息的数组下标.
JDK动态代理: (1)代理类继承 Proxy 类, 并且实现委托类接口, 主要通过代理类调用 InvocationHandler 实现类的重写方法 invoke() 来实现动态代理.
(2)只能对接口进行代理. (只能对实现接口的委托类进行代理)
(3)底层使用反射机制进行方法掉调用.
CGLib动态代理: (1)代理类继承了委托类, 在代理方法中, 会判断是否存在实现了 MethodInterceptor 接口的对象, 若存在则调用对象的 invoke() 方法, 对委托方法进行代理.
(2)不能对 final 类以及 final , private方法进行代理.
(3)底层讲方法全部放入一个数组中, 通过索引直接进行方法调用.