Spring AOP無法呼叫自身方法的原因

俺就不起網名發表於2018-08-29

1、問題概述

在使用Spring AOP時,或多或少會碰到一些方法無法被增強的問題,有時同一個類裡面的方法有的可以被增強,有的卻無法被增強。要分析原因,首先要從Spring AOP的實現機制入手。

Aop底層實現有兩種方法: 
1、基於JDK動態代理,通過介面來實現方法攔截,所以必須要確保要攔截的目標方法在介面中有定義,否則將無法實現攔截 
2、GCLib動態代理,通過動態生成子類來實現方法攔截,必須確保要攔截的目標方法可被子類訪問,即目標方法必須定義為非final。且非私有例項方法。

2、例子

服務員業務類,代理目標類

public class Waiter {
    public void greetTo(String name) {
        System.out.println("Waiter Greet to " + name);
    }

    public void serverTo(String name) {
        System.out.println("Waiter Server to " + name);
    }
}

前置增強類

public class GreetBeforeAdivce implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        // 輸出切點
        String clientName = (String) objects[0];
        System.out.println("How are you " + clientName + " ?(切點方法為:" + method + ")");
    }
}

beans-aware.xml配置

    <bean id="waiter" class="demo04.advisor.Waiter" />
    <bean id="greetBeforeAdviceAdvice" class="demo04.advisor.GreetBeforeAdivce" />
    <!--通過Advisor自動建立代理-->
    <bean id="regexpAdvisor"
          class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
          p:patterns=".*To.*" p:advice-ref="greetBeforeAdviceAdvice"  />
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"  p:proxyTargetClass="true" />

測試類

public class AopAwareTest {
    @Test
    public void autoProxy(){
        String config="com/smart/autoproxy/beans-aware.xml";
        ApplicationContext ctx=new ClassPathXmlApplicationContext(config);
        Waiter waiter=(Waiter)ctx.getBean("waiter");
        waiter.greetTo("John");
        waiter.serveTo("John");
    }
}

輸出(可以看到兩個方法都被織入了增強,增強被正確地織入匹配的連線點中)

How are you John ?(切點方法為:public void demo04.advisor.Waiter.serverTo(java.lang.String))
Waiter Server to John
How are you John ?(切點方法為:public void demo04.advisor.Waiter.greetTo(java.lang.String))
Waiter Greet to John

現在將Waiter中的serverTo()方法改造一下,讓serverTo呼叫greetTo方法,如下:

public class Waiter {
    public void greetTo(String name) {
        System.out.println("Waiter Greet to " + name);
        serverTo(name);//這裡呼叫自身的severTo方法
    }

    public void serverTo(String name) {
        System.out.println("Waiter Server to " + name);
    }
}

再次執行測試類,輸出為:

How are you John ?(切點方法為:public void demo04.advisor.Waiter.greetTo(java.lang.String))
Waiter Greet to John
Waiter Server to John
How are you John ?(切點方法為:public void demo04.advisor.Waiter.serverTo(java.lang.String))
Waiter Server to John

可以看出serveTo()和greetTo()都被織入了增強,但greetTo()內部呼叫的serverTo()沒有被織入增強。

3、原因分析

上面例子中,在greetTo()方法裡面直接呼叫serverTo(……)方法,這裡還隱含一個關鍵字,那就是this,實際上這裡呼叫是這樣的:this.serverTo(),this是當前物件。而呼叫greetTo()是的物件是被代理的,在代理物件中執行增強後,通過invoke,用實際Waiter物件來呼叫greetTo()方法執行業務邏輯。在業務邏輯內又呼叫了serverTo(……)方法,呼叫的物件是當前物件,當前物件是Waiter,問題就出在這裡,因為要想用執行serverTo方法的增強,必須用代理物件執行,但是此時卻直接用Waiter物件呼叫,繞過了代理物件增強的部分,也就是說代理增強部分失效。在同一個類中使用@Transaction,@Async並不能實現事務和非同步,道理就是這樣的。

 

相關文章