Spring AOP底層的動態代理實現有兩種方式:一種是JDK動態代理,另一種是CGLib動態代理。 JDK動態代理 JDK 1.3版本以後提供了動態代理,允許開發者在運行期創建介面的代理實例,而且只能為介面創建代理實例。 如果被代理目標沒有介面那麼Spring也無能為力,Spring通過Java的 ...
Spring AOP底層的動態代理實現有兩種方式:一種是JDK動態代理,另一種是CGLib動態代理。
JDK動態代理
JDK 1.3版本以後提供了動態代理,允許開發者在運行期創建介面的代理實例,而且只能為介面創建代理實例。
如果被代理目標沒有介面那麼Spring也無能為力,Spring通過Java的反射機制生成被代理介面的新的匿名實現類。
JDK動態代理具體實現原理:
通過實現
InvocationHandlet
介面創建自己的調用處理器;通過為Proxy類指定ClassLoader對象和一組interface來創建動態代理;
通過反射機制獲取動態代理類的構造函數,其唯一參數類型就是調用處理器介面類型;
通過構造函數創建動態代理類實例,構造時調用處理器對象作為參數參入;
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;
}