Spring AOP不攔截從對象內部調用的方法原因


  攔截器的實現原理很簡單,就是動態代理,實現AOP機制。當外部調用被攔截bean的攔截方法時,可以選擇在攔截之前或者之后等條件執行攔截方法之外的邏輯,比如特殊權限驗證,參數修正等操作。

  但是最近在項目中要在一個事務中跨數據源進行操作。數據庫跨源就不在這里說了,可以自行百度。

  這里出現的問題就是,如果我們要攔截某個類的多個方法,且在該類的方法中使用this調用要攔截的方法時會導致攔截失敗。

  這樣說有點抽象,看一個代碼:

package com.intsmaze.before;

public class AopDemo 
{

    /**
     * controller層調用的邏輯service方法,該方法內部調用分別訪問不同數據庫的service方法
     */
    public void findInforAll()
    {
        this.findDataBaseA();
        this.findDataBaseB();
    }
    /**
     * 這里我們就查詢A數據庫的某張表好了
     */
    public void findDataBaseA()
    {
        
    }
    /**
     *  這里我們就查詢B數據庫的某張表好了
     */
    public void findDataBaseB()
    {
        
    }

}

然后我們使用spring的aop寫一個進行切入的類,該類主要就是在執行findDataBaseA和findDataBaseB前執行某些代碼來告知后面的查詢操作查詢數據庫的地址。

@Aspect
public class AuthAspect
{
    // 匹配com.intsmaze.before包下AopDemo 類的
    // 所有方法的執行作為切入點
    @Before("execution(* com.intsmaze.before.AopDemo .*(..))")
    public void authority()
    {
        System.out.println("模擬執行權限檢查");
    }
}

接下來我就以比喻進行說明了,明白我的比喻的前提你要知道動態代理是什么,打個不恰當的比方吧,就是動態生成一個新的**類**(注意不是對象)。

這里我們可以這樣看當執行findInforAll的時候,AOP監測到這個方法是要被攔截的,於是生成了一個代理類,就是一個新的類。

public class AopDemo+其他名稱 
{

    /**
     * controller層調用的邏輯service方法,該方法內部調用分別訪問不同數據庫的service方法
     */
    public void findInforAll()
    {

  //---------------------------------------------------

    System.out.println("模擬執行權限檢查");

     //---------------------------------------------------
        this.findDataBaseA();
        this.findDataBaseB();
    }
    /**
     * 這里我們就查詢A數據庫的某張表好了
     */
    public void findDataBaseA()
    {    
    }
    /**
     *  這里我們就查詢B數據庫的某張表好了
     */
    public void findDataBaseB()
    {       
    }

}

  這里我們可以看到,其實生成一個新的類,該類在findInforAll方法中第一行加上了AOP指定執行的方法。我們系統中調用的就是這個代理類的findInforAll方法,而該方法中的 this.findDataBaseA();this.findDataBaseB();是不會被AOP攔截的,因為AOP攔截的包名和類名很明顯和代理類的不一樣,所以這就是為什么內部調用的方法無法攔截的原因。不知道這樣說,大家懂了沒有。關於如何用JDK寫動態代理,我會在放假時回顧以前筆記,在寫出來。

  如何解決呢?我們開始想的時避免AOP切入的類中使用this內部調用,但是發現這樣增加了代碼結構的復雜度,本來只需要一個類,最后要使用兩個類進行管理。太麻煩。

  然后考慮項目的進度,就使用把邏輯代碼封裝成工具方法進行調用。

 

在網上查資料有方法可以解決,沒有測試。

http://blog.csdn.net/quzishen/article/details/5803721下面是主要思路。

在spring的源代碼中通過一個增強對象的檢查,控制了當前的內部調用是否使用代理來執行,這讓人感到無奈。spring的作者們很隱晦的提出避免內部調用的方法。

我們可能會想,在外部調用兩次beanA,第一次調用method1,第二次調用method2,這樣做可以解決問題,但是這樣的直接后果是我們的邏輯代碼將變得紊亂,並非所有的場景下都可以通過這樣的設計來完成。雖然這是spring官方推薦的避免內部調用的idea。

查看了相關資料,得到了一種方法,即在method1的內部,通過直接獲取當前代理對象的方式然后通過代理對象調用method2,這樣觸發攔截。

看看代碼:

public void method1(){  
        logger.error("1");  
          
        // 如果希望調用的內部方法也被攔截,那么必須用過上下文獲取代理對象執行調用,而不能直接內部調用,否則無法攔截  
        if(null != AopContext.currentProxy()){  
            ((NorQuickNewsDAO)AopContext.currentProxy()).method2();  
        }else{  
            method2();  
        }         
    }  
      
    public void method2(){  
        logger.error("2");  
    }  

我們顯示的調用了AopContext來獲取當前代理對象,然后調用其方法,這樣做還必須的一個步驟是將當前的代理暴露給線程使用,在配置文件中需要配置一個參數:

<property name="exposeProxy">  
            <value>true</value>  
        </property>  

它是ProxyConfig的一個參數,默認是false,如果不設置這個參數,那么上述java代碼將無法獲取當前線程中的代理對象。

這種方法可以成功觸發攔截,但是也帶來了其他問題,比如代碼的織入,我們的代碼將變得復雜而且晦澀,而且嚴格要求系統針對於當前的bean必須配置攔截器,否則會因為找不到攔截器而拋出異常。

這樣做有什么負面影響?對事務的影響,對安全的影響,現在不得而知,還需要逐步去測試以嘗試。


免責聲明!

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



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