Spring| Spring中的動態代理配置(aspectj-autoproxy屬性介紹)

一. Spring中的代理

JDK動態代理:
其代理對象必須是某個接口的實現,它是通過運行期間創建一個接口的實現類來完成對目標對象的代理.


CGLIB代理:
實現原理類似於JDK的動態代理,只是它在運行期間生成目標類擴展的子類對象.(也就是通過繼承的方式創建的代理類).CGLIB是高效的代碼生成包,底層是依靠ASM(開源的Java字節碼編輯類庫)操字節碼實現的,性能比JDK強.


  • 基於註解使用@AspectJ風格的切面申明
@Configuration
@ComponentScan(basePackageClasses = com.yveshe.usage1.Performance.class)
@EnableAspectJAutoProxy(proxyTargetClass = false, exposeProxy = false)
public class ApplicationContextConf {

}
  • 基於XML使用@AspectJ風格的切面聲明
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="false" />
  • 基於XMl使用schema風格的切面申明
<aop:config proxy-target-class="true" expose-proxy="false" >
	...
</aop:config>

proxy-target-class 不顯示配置,默認爲false,代表使用JDK動態代理,除非目標類無實現接口,則轉爲CGLIB代理
expose-proxy 不顯示配置,默認爲false


二. 屬性說明

  • proxy-target-class:
    Spring AOP 部分使用JDK動態代理或者CGLIB來爲目標對象創建代理.(建議儘量使用JDK的動態代理),如果被代理的目標對象實現了至少一個接口,則會使用JDK動態代理,所以該目標類型實現的接口都將被代理.
    也就是說當目標對象實現了接口,則默認使用JDK的動態代理,若該目標對象沒有實現任何接口,則創建一個CGLIB代理,如果你希望強制使用CGLIB代理,則需要將該參數顯示的設置成true.
    注意:
    1.使用JDK動態代理,只能代理該目標對象實現接口的方法,並不能代理實現類中自己定義的方法.
    2.如果你希望代理目標對象的所有方法,而不只是實現自接口的方式,可以使用CGLIB代理,本質是擴展目標對象類,但是需要考慮一下兩個問題:
    1)無法通知(advise)Final方法,因爲他們不能被覆蓋寫
    2)你需要將CGLIB的jar包放在classpath下面
    3.當需要使用CGLIB代理和@AspectJ自動代理支持,使用該代碼設置<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="false" />

  • expose-proxy :
    expose-proxy。爲是否暴露當前代理對象爲ThreadLocal模式。
    有時候目標對象內部的自我調用將無法實施切面中的增強
    我們先來看一個問題: @Transacitonal註解的方法被另外一個方法調用的時候,事務是不生效的。具體不生效代碼如下

    public interface UserService{
        public void a();
        public void b();
    }
    
    public class UserServiceImpl implements UserService{
        @Transactional(propagation = Propagation.REQUIRED)
        public void a(){
            this.b();
        }
        @Transactional(propagation = Propagation.REQUIRED_NEW)
        public void b(){
            System.out.println("b has been called");
        }
    }
    

    分析:
    SpringAOP對於最外層的函數只攔截public方法,不攔截protected和private方法,另外不會對最外層的public方法內部調用的其他方法也進行攔截,即只停留於代理對象所調用的方法。所以我們會發現如果調用a方法,在b方法中出現異常時,事務不生效.
    解決辦法:
    答案就是在<aop:aspectj-autoproxy />中設置expose-proxy屬性爲true暴露代理.<aop:aspectj-autoproxy expose-proxy=“true”> ,然後使用AopContext.currentProxy()獲取當前代理,將this.b()改爲((UserService)AopContext.currentProxy()).b(),這樣就生效了。
    分析參考: https://www.cnblogs.com/chihirotan/p/7356683.html


三. 總結

  • 關於JDK和CGLIB方式的總結:
    如果目標對象實現了接口,默認情況會採用JDK自動代理實現AOP
    如果目標對象實現了接口,可以強制使用CGLIB實現AOP
    如果目標對象沒有實現接口,必須採用CGLIB庫,Spring會在JDK動態代理和CGLIB之間轉換.

  • 如何強制使用CGLIB實現AOP?
    1.添加CGLIB依賴庫
    2.在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true" />

  • JDK動態代理和CGLIB字節碼生成的區別?
    JDK動態代理只能對實現了接口的類生成代理,而不能針對類.
    CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,因爲是繼承,所以該類或方法最好不要聲明成final.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章