Around Advice

在Before Advice 與After Advice的 介紹中,您已經知道了如何在目標物件的方法呼叫前、後介入Advices的服務邏輯,事實上如果您要在方法呼叫前後加入Advices的服務邏輯,您可以 直接透過實作org.aopalliance.intercept.MethodInterceptor介面,於方法呼叫前、後執行相關的服務,而不用分 別提供Before Advice與After Advice,MethodInterceptor介面的定義如下:

   package org.aopalliance.intercept;
   
   public interface MethodInterceptor {
       public Object invoke(
           MethodInvocation methodInvocation) throws Throwable;
   }

注意到MethodInterceptor的package名稱是org.aopalliance.intercept,所以可以得知這個介面是由AOP Alliance所制訂,這表示實作MethodInterceptor介面的類別,將可以相容於遵守AOP Alliance規範的AOP框架。

與Before Advice及After Advice不同的是,在MethodInterceptor的invoke()方法中,您要自行決定是否使用 org.aopalliance.intercept.MethodInvocation的 proceed()方法來呼叫目標物件的方法,proceed()會返回目標物件的方法執行後的執行結果物件,所以在invoke()結束之前,您會有機 會修改這個結果物件,或是返回另一個完全不相干的物件,當然的,只有在真正必要的時候才會這麼作。

實際來看看如何實作MethodInterceptor,可以直接在之前的AfterAdvice中進行撰寫:

LogInterceptor.java

   package onlyfun.caterpillar;
   
   import java.util.logging.Level;
   import java.util.logging.Logger;
   import org.aopalliance.intercept.MethodInterceptor;
   import org.aopalliance.intercept.MethodInvocation;
   
   public class LogInterceptor implements MethodInterceptor {
       private Logger logger =
           Logger.getLogger(this.getClass().getName());
  
      public Object invoke(MethodInvocation methodInvocation)
                                          throws Throwable {
       logger.log(Level.INFO,
          "method starts..." + methodInvocation.getMethod());
      
       Object result = null;
      
       try {
         result = methodInvocation.proceed();
       }
       finally {
           logger.log(Level.INFO,
              "method ends..." +
              methodInvocation.getMethod() + "\n");
       }
   
       return result;
      }
   }

可以用這個Interceptor來取代先前的LogBeforeAdvice與LogAfterAdvice,Bean定義檔的撰寫方式如下所示:

beans-config.xml

   <?xml version="1.0" encoding="UTF-8"?>
   <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN"
     "http://www.springframework.org/dtd/spring-beans.dtd">
   
   <beans>
   <bean id="logInterceptor"
         class="onlyfun.caterpillar.LogInterceptor"/>
  
   <bean id="helloSpeaker"
         class="onlyfun.caterpillar.HelloSpeaker"/>
  
   <bean id="helloProxy"
         class="org.springframework.aop.framework.ProxyFactoryBean">
       <property name="proxyInterfaces">
           <value>onlyfun.caterpillar.IHello</value>
       </property>
       <property name="target">
           <ref bean="helloSpeaker"/>
       </property>
       <property name="interceptorNames">
           <list>
               <value>logInterceptor</value>
           </list>
       </property>
   </bean>
   </beans>

執行結果與AfterAdvice的執行結果是一樣的。

在Spring中,在真正執行某個方法前,會先插入Interceptor,如果有多個Interceptor,每個Interceptor會執行自己的 處理,然後再執行MethodInvocation的 proceed()方法,這將執行流程轉給下一個Interceptor,如果沒有下一個Interceptor了,就執行真正呼叫的方法,方法執行過 後,再一層一層返回Interceptor堆疊,最後離開堆疊返回應用程式本身的流程。