Spring AOP底层的动态代理实现有两种方式:一种是JDK动态代理,另外一种是CGLib动态代理。java
JDK动态代理spring
JDK 1.3版本之后提供了动态代理,容许开发者在运行期建立接口的代理实例,并且只能为接口建立代理实例。
若是被代理目标没有接口那么Spring也无能为力,Spring经过Java的反射机制生成被代理接口的新的匿名实现类。编程
JDK动态代理具体实现原理:函数
经过实现InvocationHandlet
接口建立本身的调用处理器;性能
经过为Proxy类指定ClassLoader对象和一组interface来建立动态代理;代理
经过反射机制获取动态代理类的构造函数,其惟一参数类型就是调用处理器接口类型;code
经过构造函数建立动态代理类实例,构造时调用处理器对象做为参数参入;对象
CGLib动态代理接口
CGLib 全称 Code Generation Library
,是一个强大的高性能字节码生成类库,能够实现运行期动态扩展Java类。
Spring在运行期采用CGLib的字节码技术为类建立一个子类,并在子类中拦截全部父类方法的调用,织入横切逻辑实现AOP面向切面编程。开发
注意事项
若是被代理的对象实现了接口,那么Spring默认会使用JDK动态代理,不然会强制使用CGLib实现动态代理(若是被代理的类被final关键字所修饰,那么代理会失败)
关于二者的性能,JDK动态代理所建立的代理对象,在1.8之前的版本中性能并不高,最新版本中性能获得了很大的提高,和CGLib相差不大。
Spring Boot中没法正常启用JDK动态代理的问题
关于Spring的默认动态代理模式,官方文档中显示是JDK动态代理,但Spring Boot 2.2中发现即便强制指定@EnableAspectJAutoProxy(proxyTargetClass = false)
,生成的代理类依然显示$EnhancerBySpringCGLIB。
在DefaultAopProxyFactory
中发现isProxyTargetClass
被指定为强制代理目标类,因此会采用ObjenesisCglibAopProxy
建立代理。
最后跟踪到ValidationAutoConfiguration
中的一个Bean方法中,主动读取了环境变量spring.aop.proxy-target-class
,并且默认值是true。
问题点算是找到了,不过这里只是一个函数校验的处理器,居然会强制读取魔法配置,有些莫名其妙...手工添加配置后JDK代理恢复正常。
@Bean @ConditionalOnMissingBean public static MethodValidationPostProcessor methodValidationPostProcessor(Environment environment, @Lazy Validator validator) { MethodValidationPostProcessor processor = new MethodValidationPostProcessor(); boolean proxyTargetClass = environment.getProperty("spring.aop.proxy-target-class", Boolean.class, true); processor.setProxyTargetClass(proxyTargetClass); processor.setValidator(validator); return processor; }