Java动态代理
JDK动态代理
- 生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
- 优点:代理创建过程很快,内置实现中defineClass()方法被定义为native实现
- 缺点:只能代理实现了接口的类,不是实现接口类是无法代理
cglib动态代理
- cglib采用字节码生成的方式来在代理类中调用原类方法,JDK Proxy 则是使用反射调用,由于反射存在额外security check 的开销,目前jvm jit对反射的内联支持不够好
- JDK Proxy在性能上弱于cglib
- 利用asm开源包,对代理对象类的class文件加载进来,通过字节码技术为类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑
- 优点:函数调用性能快,cglib封装了asm,可以基于类、接口,由于是加载
- 缺点:生成的字节码体量大
- cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理
spring 中的动态代理
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
- 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
#最好不要使用 private方法修饰对象和类,在AOP 的时候可能出问题
- Spring不管使用的是JDK动态代理还是CGLIB动态代理,一个是针对实现接口的类,一个是通过子类实现。无论是接口还是父类,显然都不能出现private方法,否则子类或实现类都不能覆盖到。
- 如果方法为private,那么在代理过程中,根本找不到这个方法,引起代理对象创建出现问题,也导致了有的对象没有注入进去。
- 所以如果方法需要使用AOP注解,请把它设置为非private方法
Spring 动态代理失效的问题
- 方法内嵌调用
- ((X)AopContext.currentProxy()).B() 即只有exposeProxy为true时,才会把proxy动态代理对象设置到AopContext上下文中,这个配置默认是false。
<aop:aspectj-autoproxy proxy-target-class=”true” expose-proxy=”true”/>
@EnableAspectJAutoProxy(proxyTargteClass = true, exposeProxy = true) - <tx:annotation-driven …/>
- 通过ApplicationContext来获得动态代理对象