动态代理
基本原理
JDK 动态代理
利用拦截器(拦截器必须实现 InvocationHanlder) + 反射机制生成一个实现代理接口的匿名类。根据反射获取到的信息动态生成.java 文件,然后动态编译生成.class 文件。
CGLIB 动态代理
利用 ASM 开源包,对代理对象类的 class 文件加载进来,通过修改字节码生成子类。
应用
JDK 动态代理和 CGLIB 字节码生成的区别
- JDK 动态代理只能对实现了接口的类生成代理,而不能针对类。
- CGLIB 是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。
所以 CGLib 不能对声明为 final 的方法进行代理。
效率比较
- CGLib 底层采用 ASM 字节码生成框架,使用字节码技术生成代理类,在 jdk6 之前比使用 Java 反射效率要高/
- 在 jdk6、jdk7、jdk8 逐步对 JDK 动态代理优化之后,在调用次数较少的情况下,JDK 代理效率高于 CGLIB 代理效率。
- 优先 jdk 代理,每一次 jdk 版本升级,jdk 代理效率都得到提升,而 CGLIB 代理消息确有点跟不上步伐。
Spring 如何选择用 JDK 还是 CGLIB
- 当 Bean 实现接口时,Spring 就会用 JDK 的动态代理。
- 当 Bean 没有实现接口时,Spring 使用 CGlib 是实现。
- 可以强制使用 CGlib(在 spring 配置中加入<aop:aspectj-autoproxy proxy-target-class=”true”/>)
三连问
- JDK 动态代理有什么使用条件?
JDK 动态代理只能对实现了接口的类进行代理
- 为什么 JDK 动态代理对象类必须要实现接口?
因为这种方式生成的代理类继承了 Proxy 类,且 JAVA 无法多继承,只能通过实现接口的方式来代理。
- 为什么 JDK 动态代理生成的类继承 Proxy,导致代理对象必须要有接口。
- 代理模式中的代理类只做转发处理,方法执行转发给原类,增强程序的执行转发给拦截器。
- 从效率上来看直接代理类比转发接口更消耗内存。
- 这也是为什么 JDK 动态代理需要传入被代理类的实例划对象,而不是直接传入被代理类 Class 对象。
Simple is Awesome