Spring框架中的JDK與CGLib動態代理

我們在使用Spring框架中的getBean();從容器中獲取得到代理類時有時會出先這種錯誤 org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'xx' available

 此時要明白爲什麼出錯,我們需要理解Spring中JDK代理和CGlib代理的區別。JDK其代理類是目標類所實現的接口的實現類,CGlib動態代理所產生的代理類是目標類的子類。我們來寫一個例子通過檢驗輸出來理解他們,

我們先簡單創建一個接口和他的實現類

創建一個aop,讓他輸出他們 的方法名

然後我們在Spring配置文件中寫入如下代碼

  <context:component-scan base-package="com.jd"></context:component-scan>
  
  <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

接下來創建一個測試類來檢測一下

public static void main(String[] args) {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
    IMathService mathService = applicationContext.getBean(IMathService.class);
    System.out.println(mathService.getClass());
    applicationContext.close();
}

運行

此時我們可以看到從容器中獲得的mathService對象的類爲代理對象創建的代理類,注意如果上述代碼中改爲getBean(MathService.class)則會拋出文章開頭所說明的異常。

 

我們來看一下XML配置中的這一行代碼

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

很明顯的看出這行代碼是讓aop自動創建代理類及代理對象,而這行代碼有一個屬性 proxy-target-class="false" 默認情況下是false即JDK代理,當改爲true時爲CGlib代理。

 

<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>

將該屬性改爲true依舊執行上述test。得到結果

從這可以看出CGlib既可以使用接口創建代理也可以通過實現類創建代理。

 

要明白這一現象背後的原理我們需要再深入一點發現。

在使用CGlib代理時我們打印代理類的父類發現

CGlib動態代理所產生的代理類是目標類的子類

 

再將代理設置爲JDK代理<aop:aspectj-autoproxy proxy-target-class="false">

發現jdk動態代理產生的代理類與目標類無繼承關係

而我們通過反射得到他實現的接口

可以看出JDK其代理類是目標類所實現的接口的實現類

 

這樣我們就可以明白了爲什麼在默認JDK情況下我們只能使用接口來創建代理對象,代理類只是接口的實現類,與目標類無繼承關係,所以只能用接口來創建。

而使用CGlib時代理類是目標類的子類,而目標類也是實現了接口,所以既可以使用接口創建也可以通過目標類創建。

 

這裏說明一下代理類創建代理對象兩個因素

1.xml裏面需要配置代理。

2.調用切面類的方法與切面類的表達式相匹配

若將xml配置裏的<aop:aspectj-autoproxy></aop:aspectj-autoproxy>刪掉則運行上述Test結果會

而不改動xml配置文件我們新創建一個Service但是不改動aop中的表達式

執行test

可以看出缺少任意一個因素就無法讓代理類創建代理對象

 

注意:在事務中一個類中的方法被@transationa修飾,則spring自動爲該類創建代理類及其代理對象,且創建的爲JKD代理對象。若想要變爲CGlib則在xml配置中加入<aop:aspectj-autoproxy proxy-target-class="true">即可

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