AroundAdvice


Before AdviceAfter 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堆疊,最後離開堆疊返回應用程式本身的流程。