Spring AOP嵌套調用的問題 (同一類方法內調用切面切不到)

轉載地址:https://my.oschina.net/stephenzhang/blog/664593

在開發基於 Spring 的應用的過程中碰到了一個讓我困惑了好久的問題,我在一個 Service 類doSomething1() 方法中通過this.doSomething2(); 語句調用了同一個類中的 doSomething2 方法,運行時通過調試發現 doSomething1 方法的執行前後正常地執行了自定義的 around 裝備,但是在 doSomething2 方法執行前後並未如我所期望的那樣執行自定義的 around advice 。

今天終於恍然大悟,把它當作筆記寫下來。Spring 的代理實現有兩種:一是基於 JDK Dynamic Proxy 技術而實現的;二是基於 CGLIB 技術而實現的。今天的目標是探索基於 JDK Dynamic Proxy 的動態代理。首先來看看如何自己動手實現一個對 Service Bean 對象的動態代理。爲了能夠更清楚地看到 Spring AOP 動態代理的本質,我決定不使用 JDK 中提供的 Dynamic Proxy API,就使用最普通的 java 代碼來模擬一個動態代理實例。

首先,來創建一個需要代理的接口:

 

package demo.interf;    
public interface ICustomerService {  
    public void doSomething1();  
    public void doSomething2();  
}

 然後就是具體服務類:

 

package demo.interf.impl;  
  
import demo.interf.ICustomerService;  
  
public class CustomerServiceImpl implements ICustomerService {  
  
    public void doSomething1() {  
  
        System.out.println("Inside CustomerServiceImpl.doSomething1()");  
  
        doSomething2();  
  
    }  
  
    public void doSomething2() {  
  
        System.out.println("Inside CustomerServiceImpl.doSomething2()");  
  
    }  
  
}

 下面我們就來模擬動態生成代理類的過程,若使用 JDK Dynamic Proxy,這一過程是在運行時進行的。

 

CustomerServiceImpl 類對象的代理類:  

 

package demo.interf.impl;  
  
import demo.interf.ICustomerService;  
  
public class CustomerServiceProxy implements ICustomerService {  
  
    private ICustomerService customerService;  
  
    public void setCustomerService(ICustomerService customerService) {  
        this.customerService = customerService;  
    }  
  
    public void doSomething1() {  
        doBefore();  
        customerService.doSomething1();  
        doAfter();  
    }  
  
    public void doSomething2() {  
        doBefore();  
        customerService.doSomething2();  
        doAfter();  
    }  
  
    private void doBefore() {  
        // 例如,可以在此處開啓事務  
        System.out.println("do some important things before...");  
    }  
  
    private void doAfter() {  
        // 例如,可以在此處提交或回滾事務、釋放資源等等  
        System.out.println("do some important things after...");  
    }  
  
}

 使用代理對象調用業務邏輯操作的客戶端程序:

 

package test;  
  
import demo.interf.ICustomerService;  
import demo.interf.impl.CustomerServiceImpl;  
import demo.interf.impl.CustomerServiceProxy;  
  
public class TestProxy {  
      
    public static void main(String[] args) {  
        // 創建代理目標對象。對於Spring來說,這一工作  
        // 是由Spring DI容器完成的。  
        ICustomerService serviceProxyTarget = new CustomerServiceImpl();  
  
        // 創建代理對象。對於Spring來說,這一工作  
        // 也是由Spring DI容器完成的。  
        CustomerServiceProxy serviceProxy = new CustomerServiceProxy();  
        serviceProxy.setCustomerService(serviceProxyTarget);  
        ICustomerService serviceBean = (ICustomerService) serviceProxy;  
  
        // 調用業務邏輯操作  
        serviceBean.doSomething1();  
    }  
}

現在以調試方式運行這個應用,你會發現在 doSomething1() 中調用 doSomething2() 方法的時候並未去執行CustomerServiceProxy 類的 doBefore()、doAfter() 方法。再來看看這句關鍵代碼:doSomething2(); 把它隱含的意思也表達出來吧:this.doSomething2(); 哦,我明白了,在 CustomerServiceImpl 類中 this 關鍵字表示的是當前這個CustomerServiceImpl類的實例。那程序當然就會去執行 CustomerServiceImpl 類中的 doSomething2() 方法了,而不會去執行 CustomerServiceProxy 類中的 doSomething2() 方法!!

在使用 Spring AOP 的時候,我們從 IOC 容器中獲取的 Service Bean 對象其實都是代理對象,而不是那些 Service Bean 對象本身,也就是說獲取的並不是被代理對象或代理目標。當我在自己的 Service 類中使用 this 關鍵字嵌套調用同類中的其他方法時,由於 this 關鍵字引用的並不是該 Service Bean 對象的代理對象,而是其本身,故 Spring AOP 是不能攔截到這些被嵌套調用的方法的。

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