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代理
再看一下結果

當我們指定用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的代理。有興趣的童鞋可以研究一下。
浙公網安備 33010602011771號