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; }