spring---aop(7)---Spring AOP中expose-proxy介紹


寫在前面

  expose-proxy。為是否暴露當前代理對象為ThreadLocal模式。

  SpringAOP對於最外層的函數只攔截public方法,不攔截protected和private方法(后續講解),另外不會對最外層的public方法內部調用的其他方法也進行攔截,即只停留於代理對象所調用的方法。

 

案例分析

public class AServiceImpl implements AService{
    @Override
    public void barA() {
        System.out.println("AServiceImpl.barA()");  
        barB();
    }
    @Override
    public void barB() {
         System.out.println("AServiceImpl.barB()");  
    }
}

 

控制台的輸出結果:

run my before advice
AServiceImpl.barA()
AServiceImpl.barB()

分析:

  發現aop並沒有對barB方法進行增強,只是增強了barA方法。

  判斷上述this.barB()方法是否被攔截的最本質的東西是看this到底是誰?有如下對象B類的對象b,和cglib生成的代理對象bProxy,代理對象bProxy內部擁有b。如果調用b對象的任何方法,肯定不會發生任何攔截,當調用bProxy的方法則都會進入攔截函數。

  當我們調用bProxy對象的barA()方法時,先執行cglib之前設置的callback對象的intercept攔截函數,如下: (之前的文章已經分析過代碼)

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object oldProxy = null;
            boolean setProxyContext = false;
            Class<?> targetClass = null;
            Object target = null;
            try {
                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
                target = getTarget();
                if (target != null) {
                    targetClass = target.getClass();
                }
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                Object retVal;
                if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                    retVal = methodProxy.invoke(target, args);
                }
                else {
                    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                }
                retVal = processReturnType(proxy, target, method, retVal);
                return retVal;
            }
            finally {
                if (target != null) {
                    releaseTarget(target);
                }
                if (setProxyContext) {
                    AopContext.setCurrentProxy(oldProxy);
                }
            }
        }

  這個過程之前的文章已經分析過,這里就是首先取出攔截器鏈List<Object> chain,當barA方法不符合我們所配置的pointcut時,攔截器鏈必然為空,然后就是直接執行目標對象的方法。 
當barA方法符合所配置的pointcut時,攔截器鏈不為空,執行相應的通知advice,currentInterceptorIndex 從-1開始,如下(之前的文章已經分析過代碼)

public Object proceed() throws Throwable {
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }
        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            else {
                return proceed();
            }
        }
        else {
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }

  隨着通知不斷的傳遞執行,最終this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1將會滿足條件,將會來到執行目標對象的方法invokeJoinpoint(): 

protected Object invokeJoinpoint() throws Throwable {
            if (this.publicMethod) {
                return this.methodProxy.invoke(this.target, this.arguments);
            }
            else {
                return super.invokeJoinpoint();
            }
        }

  在這里不管要攔截的目標方法是不是public方法,最終所傳遞的對象都是this.target,他是目標對象而不是代理對象,即執行上述barA()函數的對象是目標對象而不是代理對象,所以它內部所調用的this.barB()也是目標對象,因此不會發生攔截,如果是執行的是代理對象.barB()則必然會進入intercept攔截過程。所以上述調用barA函數,其內部調用的barB函數是不會發生攔截的,因為this指的是目標對象,不是代理對象。 

 

 

實現BarB攔截

  如果你想實現barA調用時內部的BarB也進行攔截,就必須把this換成代理對象。這時就要用到了,xml配置中的expose-proxy="true",即暴露出代理對象,它使用的是ThreadLocal設計模式,我們可以這樣獲取代理對象,AopContext.currentProxy()就是代理對象,然后轉換成目標對象或者目標接口,執行相應的方法:

public class AServiceImpl implements AService{
    @Override
    public void barA() {
        System.out.println("AServiceImpl.barA()");  
        AService proxy=(AService) AopContext.currentProxy();
        proxy.barB();
    }
    @Override
    public void barB() {
         System.out.println("AServiceImpl.barB()");  
    }
}
    <bean id="aServiceImplProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="exposeProxy">
            <value>true</value>
        </property>
        <property name="interfaces" value="com.xxx.plus.aop.demo.AService"/>
        <property name="target">
            <ref bean="aServiceImpl"/>
        </property>
         <property name="interceptorNames">  
            <list>  
                <value>myBeforAdvice</value>  
            </list>  
        </property>  
    </bean>

執行結果:

run my before advice
AServiceImpl.barA()
run my before advice
AServiceImpl.barB()

分析:

  barA()和barB都被aop攔截實現了增強

 

最后再給出Spring的文檔說明:

  Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only! 
  If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision.

 


免責聲明!

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



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