來(lái)使用Spring的AOP框架來(lái)重新撰寫之前「從代理機(jī)制初探AOP」主題中的程式,首先,Spring AOP預(yù)設(shè)使用動(dòng)態(tài)代理來(lái)實(shí)作AOP,動(dòng)態(tài)代理必須宣告被代理物件的介面:
代碼:
package onlyfun.caterpillar;
publicinterface IHello {
public void hello(String name);
public void morning(String name);
}
實(shí)作IHello介面的HelloSpeaker則如下:
代碼:
package onlyfun.caterpillar;
public class HelloSpeaker implements IHello {
public void hello(String name) {
System.out.println("Hello, " + name);
}
public void morning(String name) {
System.out.println("Morning, " + name);
}
}
我們希望在方法執(zhí)行前後進(jìn)行記錄的動(dòng)作,在AOP中這需要Around類型的Advice,通常的,實(shí)作Around類型的策略是使用Interceptor。
Interceptor的一個(gè)例子是Servlet中的Filter機(jī)制,在Filter機(jī)制下,當(dāng)請(qǐng)求來(lái)臨時(shí),會(huì)被Filter先攔截並進(jìn)行處理,之後傳給下一個(gè)Filter,最後才是真正處理請(qǐng)求的Servlet。實(shí)作AOP時(shí)所使用的Interceptor策略與Filter類似,所不同的是Filter被綁定於Servlet API,且只適用於請(qǐng)求階段。
您可以將一連串的Interceptor想成是堆疊結(jié)構(gòu),在Spring中,在真正執(zhí)行某個(gè)方法前,會(huì)先插入Interceptor,每個(gè)Interceptor會(huì)執(zhí)行自己的處理,然後再執(zhí)行proceed,這將執(zhí)行流程轉(zhuǎn)給下一個(gè)Interceptor,如果沒(méi)有下一個(gè)Interceptor了,就執(zhí)行真正呼叫的方法,方法執(zhí)行過(guò)後,再一層一層返回Interceptor堆疊,最後離開堆疊,如下圖,流程先是紅色,方法執(zhí)行後的流程是藍(lán)色:
代碼:
<-- --> interceptor1 <-- --> interceptor2 <-- --> ..... <-- --> Invoked method
Spring在實(shí)作Interceptor時(shí),是實(shí)作org.aopalliance.intercept.MethodInterceptor,AOP Alliance(http://www.sourceforge.net/projects/aopalliance
)的目標(biāo)是制定AOP功能的標(biāo)準(zhǔn)介面,目前只提供一部份的子集合,子集包括了Interceptor,Spring實(shí)作MethodInterceptor,以求將來(lái)Interceptor的可移植性。
我們實(shí)作LogInterceptor如下:
代碼:
package onlyfun.caterpillar;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.util.logging.*;
public class LogInterceptor implements MethodInterceptor {
private Logger logger = Logger.getLogger(this.getClass().getName());
publicObject invoke(MethodInvocation methodInvocation) throws Throwable {
logger.log(Level.INFO, "method starts..." + methodInvocation.getMethod());
try {
Object result = methodInvocation.proceed();
return result;
}
finally {
logger.log(Level.INFO, "method ends..." + methodInvocation.getMethod() + "\n");
}
}
}
invoke()中的MethodInvocation參數(shù)包括了下一個(gè)Interceptor的資訊、被執(zhí)行的方法、以及方法所需的參數(shù),其返回值是被呼叫方法的返回值。
我們還需要一個(gè)代理物件,這可以使用org.springframework.aop.framework.ProxyFactoryBean,它需要知道代理的介面與物件,以及所要介入的Interceptor,如下:
代碼:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-"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>
Interceptor執(zhí)行的順序是interceptorNames中設(shè)定的順序,最後是target目標(biāo)物件,使用下面的程式來(lái)測(cè)試一下:
代碼:
package onlyfun.caterpillar;
import org.springframework.context.*;
import org.springframework.context.support.*;
public class SpringTest {
publicstatic void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("bean.xml");
IHello helloProxy = (IHello) context.getBean("helloProxy");
helloProxy.hello("Justin");
helloProxy.morning("momor");
}
}
執(zhí)行結(jié)果如下:
代碼:
2004/11/26 上午 11:59:09 onlyfun.caterpillar.LogInterceptor invoke
資訊: method starts...publicabstract void onlyfun.caterpillar.IHello.hello(java.lang.String)
Hello, Justin
2004/11/26 上午 11:59:09 onlyfun.caterpillar.LogInterceptor invoke
資訊: method ends...publicabstract void onlyfun.caterpillar.IHello.hello(java.lang.String)
2004/11/26 上午 11:59:09 onlyfun.caterpillar.LogInterceptor invoke
資訊: method starts...publicabstract void onlyfun.caterpillar.IHello.morning(java.lang.String)
Morning, momor
2004/11/26 上午 11:59:09 onlyfun.caterpillar.LogInterceptor invoke
資訊: method ends...publicabstract void onlyfun.caterpillar.IHello.morning(java.lang.String)
完成了第一個(gè)Spring AOP程式了,不過(guò)這當(dāng)中還有許多細(xì)節(jié)可以說(shuō)明,下一個(gè)主題再繼續(xù)。