[toc]
SpringAOP采用动态代理机制和字节码生成技术实现。
在运行期间,为目标对象生成1个代理对象,并把横切逻辑织入到代理对象中。系统运行时用的是代理对象,而非目标对象。
复习静态代理模式:
缺点: 一旦横切逻辑变多,我们要手动添加的类也越多。如果要创建N多个代理对象,会很麻烦。另外如果加了1个方法,每个代理中也都要加,数量是很恐怖的。
java动态代理缺点: 如果类没有实现任何的接口,就无法使用动态代理生成动态代理对象。(即需要1个类似于request方法的对外接口,让外界即可用代理类访问,也可用原生类访问)
SpringAOP的动态代理#
- 如果发现对象实现了interface,就会用动态代理为其生成代理实例。
- 如果没有实现interface,则会尝试使用CGLIB的开源动态字节码生成类库,来为目标对象生成动态的代理实力。
JDK代理#
Q: 为什么jdk代理的方式必须要接口?
A:
因为java动态代理的代理生成方法Proxy.newProxyInstance中,第二个参数就是被代理对象的inteface,通过这个他才能识别对哪些方法做代理(即根据接口找到方法做代理)
而且返回值就是这个接口类型。
Info proxyInfo = (Info)Proxy.newProxyInstance(代理类对象.getClass().getClassLoader(),实际对象.handler.getInterfaces(), 代理类对象.
CGLIB动态字节码生成:#
对目标对象进行继承,生成子类,然后子类中通过覆写方法来扩展父类的方法,把横切逻辑加入, 然后让系统调用扩展后的这个子类即可。
例子:
原类:
先实现1个MethodInterceptor(继承自Callback)接口,作为扩展类,里面就包含了横切逻辑
然后通过CGLIB的Enhancer对来生成扩展子类
缺陷: 无法对final方法做覆写
Q: 如果我做了AOP, AOP的那个类里有一个private方法,这个方法是在AOP的范围内的,有什么问题?
A: 如果那个方法里用到一个autowire注入的对象,就会报空指针。
原因:
- AOP本质是继承原类,生成一个CGLIB新类, 重写里面的public或者protect方法,替换成自己切入的那个方法。
- 从public方法切入后,实际上会走进原生bean对象,因此用的autowire对象是原生bean里的东西,是注入过的。
- 而private不会重写,因此代码没变,”不会跳转到原生bean里去调用“, 而CGLIB代理对象是不会做bean注入的(因为顺序问题,先bean初始化再AOP), 所以我们调用privatge时,用的是CGLIB对象里的autowire成员,他从未被注入过。
详细源码:SpringAOP私有方法导致@Autowire注入失败原理