spring中基於JDK和CGLIB代理在項目的應用


一、環境與問題

環境

  spring boot的版本是1.2.1.RELEASE、JDK版本是1.7

問題

  ​ A服務 PeopleService 調用B服務 HelloService ,其中B服務的方法 say() 是是一個事物方法,並且B服務實現一個接口 IHelloService實際過程中發現A服務無法使用 @autowire 把B服務注入,但是去掉接口 `IHelloService 或者去掉 @Transactional 則可注入B服務,亦或者@Autowired注入的類型使用IHelloService

 

二、問題思考

  在解決上面問題時,我們必須要知道spring容器在開啟事物的過程中使用的是AOP技術,其實底層是通過代理實現的。在spring在選擇代理時默認實現了2中代理方式一種是JDK代理、另外一種是CGLIB代理。其中JDK代理是基於接口,通過接口InvocationHandler實現的,而CGLIB代理是基於類的,通過接口MethodInterceptor 來實現。

 

三、驗證思考

  為了驗證我的解決思路是否正確,去官網查看了一下文檔

10.5 Using the ProxyFactoryBean to create AOP proxies
   JavaBean properties
   In common with most FactoryBean implementations provided with Spring, the ProxyFactoryBean
class is itself a JavaBean. Its properties are used to:
Specify the target you want to proxy.
Specify whether to use CGLIB.

上面這段話的大體意思:一般情況下面我們使用FactoryBean來提供bean,當使用AOP時會使用ProxyFactoryBean來提供bean。我們可以指定目標(即需要代理的對象),也可指定是否使用CGLIB代理。換而言之,spring默認使用的是JDK的代理。

再看一下實際的A服務的bean

 

果然是如我們猜想的那樣使用了JDK代理。

 

四、解決問題

指定使用CGLIB代理

@Scope(proxyMode=ScopedProxyMode.TARGET_CLASS)

再看一下結果

 

當我們指定用CGLIB代理之后,發現HelloService 可以被正常進行注入,並且HelloService 也由JDK代理對象變成了CGLIB代理對象。

 

五、問題深入與擴展

  ​ 隨着問題的深入,我們沒有解決為什么使用JDK的代理對象不行,而使用CGLIB代理對象卻可以?

  不知道大家有沒有注意我上面講過一句話“JDK代理是基於接口...,而CGLIB代理是基於類的...”,問題就出現在這邊。下面我把基於JDK和CGLIB的對象的類名、父類名及接口名打印出來:

  HelloService 基於JDK的代理方式:

 

基於JDK代理的helloService對象的類名$Proxy44,父類是Proxy,接口是IHelloService

  HelloService 基於CGLIB的代理方式:

 

基於CGLIB代理的helloService對象的類名是HelloService$$EnhancerBySpringCGLIB$$80f67bbd 父類是HelloService

至此我們終於知道了通過@Autowired 注入HelloService 對象時,使用JDK代理時代理對象實現了IHelloService接口,而使用CGLIB代理時代理對象是繼承了HelloService

 

  spring中的代理的問題告一段落了,孔夫子講過"舉一隅,不以三隅反,則不復也"。那么我們實際項目中還有那些耳熟能詳的框架也使用了代理了呢?

在web開發中不知道大家有沒有注意到mybatis中只有接口而沒有實現類,其實也是使用了JDK的代理。有興趣的童鞋可以研究一下。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM