CGLib动态代理的实现

使用JDK创建动态代理有一个限制,  即它只能为接口创建代理实例.  对于没有定义接口的业务方法的类,  使用CDGlib 进行动态代理.

CGLib是一个强大的, 高性能的代码生成库.  被广泛应用于 AOP 框架. 用以提供方法拦截操作.

CGLib采用底层的字节码技术,  可以为一个类创建子类,  在子类中采用方法拦截的技术拦截所有父类方法的调用,  并织入横切逻辑.

一. CGLib 动态代理过程

CGLib动态代理的实现_第1张图片

CGLib 定义的 intercept() 接口方法, 拦截所有目标类方法的调用.  其中 o 代表目标类的实例,  method 为目标类方法的反射;  args为方法的动态入参,  proxy为代理类实例.

CGLib动态代理的实现_第2张图片

代理对象的生成由 Enhancer 类实现. Enhancer是CGLib的字节码增强器. 可以很方便的对类进行扩展.

Enhancer 创建代理对象的大概步骤如下:

1. 生成代理类 Class 二进制字节码.

2.通过 Class.forname() 加载字节码文件, 生成 Class 对象.

3.通过反射机制获得实例构造, 并创建代理类对象.

二. 生成代理类对象

通过反编译可以查看代理类的实现, 如下:  (此处借鉴他人博客内容)

CGLib动态代理的实现_第3张图片

CGLib动态代理的实现_第4张图片

1. 生成的代理类 UserService$$EnhancerByCGLIB$$394dddeb 继承了被代理类.  

如果委托类被 final 修饰, 那么它不可被代理;  如果委托类中存在被 final 修饰的方法, 那么该方法也不可被代理.

2. 代理类为委托方法生成两个方法,  以上述代码中的add()为例,  一个是重写的add() (代理类重写了委托类中的方法),  另一个是  CGLIB$add$0 方法. 这个方法直接调用父类的 add()方法.

3. 当执行代理对象的 add() 方法时, 首先判断一下是否存在实现了 MethodIntercetpor 接口的对象 cglib$CALLBACK_0,   如果存在则调用 MethodInterceptor 对象的 intercept() 方法.

CGLib动态代理的实现_第5张图片

代理类为每个委托方法都生成两个方法,  如上代码所示.

4.每个被代理的方法都应有一个  MethodProxy 对象,  methodProxy.invokeSuper() 方法最终调用委托类的方法.

三. 调用委托方法是通过代理方法的 MethodProxy 对象调用 invokeSuper() 方法来执行的.

Method 类中的 invokeSuper() 方法.

CGLib动态代理的实现_第6张图片

现在还看不出来 invokeSuper() 方法如何执行委托类中方法.  在 CGLib 中, 对委托类方法的调用不是通过反射机制实现的,  而是直接对方法进行调用,  通过内部类 FastClassInfo 来调用委托类中的方法.

FastClassInfo 内部类的机制:

CGLib动态代理的实现_第7张图片 

以上述代码 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动态代理和 CGLib 动态代理的特点:

JDK动态代理:  (1)代理类继承 Proxy 类, 并且实现委托类接口, 主要通过代理类调用 InvocationHandler 实现类的重写方法 invoke() 来实现动态代理. 

(2)只能对接口进行代理. (只能对实现接口的委托类进行代理)

(3)底层使用反射机制进行方法掉调用.

CGLib动态代理:  (1)代理类继承了委托类, 在代理方法中, 会判断是否存在实现了 MethodInterceptor 接口的对象, 若存在则调用对象的 invoke() 方法, 对委托方法进行代理. 

(2)不能对 final 类以及 final , private方法进行代理.

(3)底层讲方法全部放入一个数组中, 通过索引直接进行方法调用.

你可能感兴趣的:(CGLib动态代理的实现)