Spring AOP 依靠 JDK 和 CGLib 进行动态代理实现。在此对两种实现方式的一些知识进行整理。
JDK
使用示例
1 | Java复制代码/** |
1 | Java复制代码/** |
1 | Java复制代码public class Main { |
可见代理成功。
概括一下,动态代理的方式一般为:
- 继承
InvocationHandler
,重写方法invoke
- 执行
Proxy.newProxyInstance
生成动态代理类
invoke 方法
由上可以看出,proxy 成功对 Iinterface 接口进行代理,但是在使用时,我们并未见到 InvocationHandler 中 invoke
方法的调用,动态代理是如何执行 invoke
的呢?
采用其他资料生成的案例,以下是代理类的反编译代码。
首先来看看代理类的继承关系:
可以看到代理类继承了 Proxy ,再来看看代理类中的方法调用,其中 teach()
是被代理接口的方法声明,内部只是简单地调用了父类即 Proxy 的 h 属性的 invoke
方法
再回看 Proxy 类的属性
可以猜测,对被代理方法进行调用时,会转而由被代理类中继承自 Proxy 的 InvocationHandler 执行,从而 invoke
方法被调用。再来看看调用的 newProxyInstance
的逻辑,代码进行了省略,添加了简单注释
1 | java复制代码public static Object newProxyInstance(ClassLoader loader, |
CGLib
使用示例
1 | java复制代码class Hello { |
1 | java复制代码class MyInterceptor implements MethodInterceptor { |
1 | java复制代码public class Main { |
概括一下,CGLib 动态代理的方式一般为:
- 构造 Enhancer
- 设置代理对象(作为父类)
- 设置代理策略
- 创建代理对象
Callback
参考链接:blog.csdn.net/zhang662205…
Callback 可以理解成生成的代理类的方法被调用时会执行的逻辑,具有以下六种方式:
- NoOp:不做任何操作
- FixedValue:要求实现接口的
loadObjecd
方法,重写了被代理类的响应方法,同时,要求返回值和方法返回值相同,否则会抛出类型转换异常。此方式看不到人喝原方法的信息,也无法调用原方法。 - MethodInterceptor:类似 AOP 的环绕增强,代理类的方法调用都会转入执行该接口的
intercept
方法。需要执行原方法可以使用参数 method 进行反射调用,或者使用参数 proxy(proxy会快一些) - InvocationHandler:类似 MethodInterceptor,若自定义该接口的
invoke
方法,需要注意参数 method 的invoke
方法,会无限循环调用 - LazyLoader:调用时,返回一个代理对象并存储负责所有的该代理类调用,类似 Spring 的 singleton
- Dispatcher:每次调用都会返回一个新的代理类,类似 Spring 的 prototye
JDK 与 Cglib 的对比
JDK | Cglib | |
---|---|---|
代理目标 | 接口 | 类(被代理类会作为父类,无法处理 final) |
实现 | 反射机制 | 运行过程中修改被代理类的 class 文件字节码 |
Spring采用策略 | 目标对象实现接口时采用 | 目标对象未实现接口时采用 |
- 默认为 JDK ,使用时可以进行指定
本文转载自: 掘金