Spring AOP 什么时候用 JDK 动态代理、什么时候用 CGLIB?Boot 里的默认值为什么不同?两种代理对 instanceof 和按类注入有什么影响?创建和调用的成本各是多少?这篇在 Spring Framework 7.0.9 上逐项验证。
@EnableAspectJAutoProxy(默认):
WithIface(有接口)的 Bean:JDK 动态代理,类名 jdk.proxy2.$Proxy17;instanceof Greeter=true;instanceof WithIface=false
NoIface(没有接口)的 Bean:CGLIB 代理,类名 NoIface·SpringCGLIB·N;instanceof NoIface=true
按具体类 WithIface 取 Bean:✗ NoSuchBeanDefinitionException有接口走 JDK 代理,没有接口走 CGLIB;JDK 代理不是实现类的实例。
@EnableAspectJAutoProxy(proxyTargetClass = true):
WithIface(有接口)的 Bean:CGLIB 代理,类名 WithIface·SpringCGLIB·N;instanceof Greeter=true;instanceof WithIface=true
NoIface(没有接口)的 Bean:CGLIB 代理,类名 NoIface·SpringCGLIB·N;instanceof NoIface=true
按具体类 WithIface 取 Bean:成功设成 true 后所有代理都是 CGLIB,按具体类注入可用。
Boot(@EnableAutoConfiguration,没有设任何 spring.aop.* 属性):
WithIface(有接口)的 Bean:CGLIB 代理,类名 WithIface·SpringCGLIB·N;instanceof Greeter=true;instanceof WithIface=true
NoIface(没有接口)的 Bean:CGLIB 代理,类名 NoIface·SpringCGLIB·N;instanceof NoIface=true
按具体类 WithIface 取 Bean:成功
spring.aop.proxy-target-class 的值 = (没设)Boot 的 AOP 自动配置默认就用 CGLIB,不需要设属性。
JDK 代理的类实现的接口:[Greeter, SpringProxy, Advised, DecoratingProxy];能否强转成 WithIface:false CGLIB 代理的父类:WithIface;能调用 extra()(接口里没有的方法):extra
JDK 代理只实现接口,CGLIB 代理继承目标类。
为同一个目标类创建 2000 个代理:JDK 135 ms(新加载 1163 个类);CGLIB 69 ms(新加载 347 个类) 每次调用 hi()(代理上挂一个什么都不做的 MethodInterceptor):JDK 代理 42 ns;CGLIB 代理 41 ns;直接调用 1.4 ns(sink=36000000) 为同一个目标类创建 2000 个代理:JDK 127 ms(新加载 1163 个类);CGLIB 66 ms(新加载 347 个类) 每次调用 hi()(代理上挂一个什么都不做的 MethodInterceptor):JDK 代理 47 ns;CGLIB 代理 41 ns;直接调用 1.4 ns(sink=36000000) 为同一个目标类创建 2000 个代理:JDK 138 ms(新加载 1163 个类);CGLIB 76 ms(新加载 347 个类) 每次调用 hi()(代理上挂一个什么都不做的 MethodInterceptor):JDK 代理 43 ns;CGLIB 代理 40 ns;直接调用 1.5 ns(sink=36000000)
调用开销两者相当,创建时 CGLIB 更快、加载的类更少。