日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

什么鬼?弃用JDK动态代理,Spring5 默认使用 CGLIB 了?

發布時間:2025/3/16 javascript 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 什么鬼?弃用JDK动态代理,Spring5 默认使用 CGLIB 了? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Spring5 AOP 默認使用 Cglib 了?我第一次聽到這個說法是在一個微信群里:

群聊天

真的假的?查閱文檔

剛看到這個說法的時候,我是保持懷疑態度的。

大家都知道 Spring5 之前的版本 AOP 在默認情況下是使用 JDK 動態代理的,那是不是 Spring5 版本真的做了修改呢?于是我打開 Spring Framework 5.x 文檔,再次確認了一下:

文檔地址:https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/core.html#aop

Spring Framework 5.x 文檔

簡單翻譯一下。Spring AOP 默認使用 JDK 動態代理,如果對象沒有實現接口,則使用 CGLIB 代理。當然,也可以強制使用 CGLIB 代理。

什么?文檔寫錯了?!

當我把官方文檔發到群里之后,又收到了這位同學的回復:

文檔寫錯了?!

SpringBoot 2.x 代碼示例

為了證明文檔寫錯了,這位同學還寫了一個 DEMO。下面,就由我來重現一下這個 DEMO 程序:

運行環境:SpringBoot 2.2.0.RELEASE 版本,內置 Spring Framework 版本為 5.2.0.RELEASE 版本。同時添加 spring-boot-starter-aop 依賴,自動裝配 Spring AOP。

public interface UserService {void work(); }@Service public class UserServiceImpl implements UserService {@Overridepublic void work() {System.out.println("開始干活...coding...");} } @Component @Aspect public class UserServiceAspect {@Before("execution(* com.me.aop.UserService.work(..))")public void logBefore(JoinPoint joinPoint) {System.out.println("UserServiceAspect.....()");} } 默認使用Cglib代理了?

UserServiceImpl實現了UserService接口,同時使用UserServiceAspect對UserService#work方法進行前置增強攔截。

從運行結果來看,這里的確使用了 CGLIB 代理而不是 JDK 動態代理。

難道真的是文檔寫錯了?!

@EnableAspectJAutoProxy 源碼注釋

在 Spring Framework 中,是使用@EnableAspectJAutoProxy注解來開啟 Spring AOP 相關功能的。

Spring Framework 5.2.0.RELEASE 版本@EnableAspectJAutoProxy注解源碼如下:

@EnableAspectJAutoProxy源碼

通過源碼注釋我們可以了解到:在 Spring Framework 5.2.0.RELEASE 版本中,proxyTargetClass的默認取值依舊是false,默認還是使用 JDK 動態代理。

難道文檔和源碼注釋都寫錯了?!

@EnableAspectJAutoProxy 的 proxyTargetClass 無效了?

接下來,我嘗試使用@EnableAspectJAutoProxy來強制使用 JDK 動態代理。

運行環境:SpringBoot 2.2.0.RELEASE 版本,內置 Spring Framework 版本為 5.2.0.RELEASE 版本。

proxyTargetClass設置無效了?

通過運行發現,還是使用了 CGLIB 代理。難道@EnableAspectJAutoProxy 的 proxyTargetClass設置無效了?

Spring Framework 5.x

整理一下思路

  • 有人說 Spring5 開始 AOP 默認使用 CGLIB 了

  • Spring Framework 5.x 文檔和 @EnableAspectJAutoProxy源碼注釋都說了默認是使用 JDK 動態代理

  • 程序運行結果說明,即使繼承了接口,設置proxyTargetClass為false,程序依舊使用 CGLIB 代理

  • 等一下,我們是不是遺漏了什么?

    示例程序是使用 SpringBoot 來運行的,那如果不用 SpringBoot,只用 Spring Framework 會怎么樣呢?

    運行環境:Spring Framework 5.2.0.RELEASE 版本。UserServiceImpl 和 UserServiceAspect 類和上文一樣,這里不在贅述。

    Spring Framework 5.xSpring Framework 5.x使用CGLIB

    運行結果表明:在 Spring Framework 5.x 版本中,如果類實現了接口,AOP 默認還是使用 JDK 動態代理。

    再整理思路

    ? ? 1. Spring5 AOP 默認依舊使用 JDK 動態代理,官方文檔和源碼注釋沒有錯。

    ? ? 2. SpringBoot 2.x 版本中,AOP 默認使用 cglib,且無法通過proxyTargetClass進行修改。

    ? ? 3. 那是不是 SpringBoot 2.x 版本做了一些改動呢?

    再探 SpringBoot 2.x

    結果上面的分析,很有可能是 SpringBoot2.x 版本中,修改了 Spring AOP 的相關配置。那就來一波源碼分析,看一下內部到底做了什么。

    源碼分析

    源碼分析,找對入口很重要。那這次的入口在哪里呢?

    @SpringBootApplication是一個組合注解,該注解中使用@EnableAutoConfiguration實現了大量的自動裝配。

    EnableAutoConfiguration也是一個組合注解,在該注解上被標志了@Import。

    @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {

    AutoConfigurationImportSelector實現了DeferredImportSelector接口。

    在 Spring Framework 4.x 版本中,這是一個空接口,它僅僅是繼承了ImportSelector接口而已。而在 5.x 版本中拓展了DeferredImportSelector接口,增加了一個getImportGroup方法:

    AutoConfigurationImportSelector#getImportGroup

    在這個方法中返回了AutoConfigurationGroup類。這是AutoConfigurationImportSelector中的一個內部類,他實現了DeferredImportSelector.Group接口。

    在 SpringBoot 2.x 版本中,就是通過AutoConfigurationImportSelector.AutoConfigurationGroup#process方法來導入自動配置類的。

    導入配置類

    通過斷點調試可以看到,和 AOP 相關的自動配置是通過org.springframework.boot.autoconfigure.aop.AopAutoConfiguration來進行配置的。

    AopAutoConfiguration源碼

    真相大白

    看到這里,可以說是真相大白了。在 SpringBoot2.x 版本中,通過AopAutoConfiguration來自動裝配 AOP。

    默認情況下,是肯定沒有spring.aop.proxy-target-class這個配置項的。而此時,在 SpringBoot 2.x 版本中會默認使用 Cglib 來實現。

    SpringBoot 2.x 中如何修改 AOP 實現

    通過源碼我們也就可以知道,在 SpringBoot 2.x 中如果需要修改 AOP 的實現,需要通過spring.aop.proxy-target-class這個配置項來修改。

    #在application.properties文件中通過spring.aop.proxy-target-class來配置 spring.aop.proxy-target-class=false spring-configuration-metadata.json

    這里也提一下spring-configuration-metadata.json文件的作用:在使用application.properties或application.yml文件時,IDEA 就是通過讀取這些文件信息來提供代碼提示的,SpringBoot 框架自己是不會來讀取這個配置文件的。

    SringBoot 1.5.x 又是怎么樣的

    SringBoot 1.5.x

    可以看到,在 SpringBoot 1.5.x 版本中,默認還是使用 JDK 動態代理的。

    SpringBoot 2.x 為何默認使用 Cglib

    SpringBoot 2.x 版本為什么要默認使用 Cglib 來實現 AOP 呢?這么做的好處又是什么呢?筆者從網上找到了一些資料,先來看一個 issue。

    Spring Boot issue #5423

    Use @EnableTransactionManagement(proxyTargetClass = true) #5423

    https://github.com/spring-projects/spring-boot/issues/5423

    在這個 issue 中,拋出了這樣一個問題:

    image.png

    翻譯一下:我們應該使用@EnableTransactionManagement(proxyTargetClass = true)來防止人們不使用接口時出現討厭的代理問題。

    這個"不使用接口時出現討厭的代理問題"是什么呢?思考一分鐘。

    討厭的代理問題

    假設,我們有一個UserServiceImpl和UserService類,此時需要在UserContoller中使用UserService。在 Spring 中通常都習慣這樣寫代碼:

    @Autowired UserService userService;

    在這種情況下,無論是使用 JDK 動態代理,還是 CGLIB 都不會出現問題。

    但是,如果你的代碼是這樣的呢:

    @Autowired UserServiceImpl userService;

    這個時候,如果我們是使用 JDK 動態代理,那在啟動時就會報錯:

    啟動報錯

    因為 JDK 動態代理是基于接口的,代理生成的對象只能賦值給接口變量。

    而 CGLIB 就不存在這個問題。因為 CGLIB 是通過生成子類來實現的,代理對象無論是賦值給接口還是實現類這兩者都是代理對象的父類。

    SpringBoot 正是出于這種考慮,于是在 2.x 版本中,將 AOP 默認實現改為了 CGLIB。

    更多的細節信息,讀者可以自己查閱上述 issue。

    總結

  • Spring 5.x 中 AOP 默認依舊使用 JDK 動態代理。

  • SpringBoot 2.x 開始,為了解決使用 JDK 動態代理可能導致的類型轉化異常而默認使用 CGLIB。

  • 在 SpringBoot 2.x 中,如果需要默認使用 JDK 動態代理可以通過配置項spring.aop.proxy-target-class=false來進行修改,proxyTargetClass配置已無效。

  • 延伸閱讀

    issue:Default CGLib proxy setting default cannot be overridden by using core framework annotations (@EnableTransactionManagement, @EnableAspectJAutoProxy) #12194

    https://github.com/spring-projects/spring-boot/issues/12194

    這個 issue 也聊到了關于proxyTargetClass設置失效的問題,討論內容包括:@EnableAspectJAutoProxy、@EnableCaching 和 @EnableTransactionManagement。感興趣的讀者可以自行查閱 issue。

    有道無術,術可成;有術無道,止于術

    歡迎大家關注Java之道公眾號

    好文章,我在看??

    總結

    以上是生活随笔為你收集整理的什么鬼?弃用JDK动态代理,Spring5 默认使用 CGLIB 了?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。