Before Advice會在目標物件的方法執行之前被呼叫,您可以實現org.springframework.aop.MethodBeforeAdvice介面來實作Before Advice的邏輯,該介面於Spring中的定義如下所示:
package org.springframework.aop;
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args,
Object target) throws Throwable;
}
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args,
Object target) throws Throwable;
}
在定義中可以看到,MethodBeforeAdvice繼承自BeforeAdvice介面,而BeforeAdvice介面又繼承自Advice介 面,後兩者都是標籤介面(Tag interface),只是用作標示而無定義任何方法,MethodBeforeAdvice繼承了BeforeAdvice,before()方法會在 目標物件(Target)上指定的方法執行之前被呼叫,您可以取得被執行的Method實例、引數及目標物件,before()方法上宣告為void,所 以不傳回任何的結果,在before()方法執行完畢之後,除非您丟出例外,否則目標物件上的方法就會被執行。
以實例來示範如何使用Before Advice,首先要定義目標物件必須實現的介面:
- IHello.java
package onlyfun.caterpillar;
public interface IHello {
public void hello(String name);
}
接著定義一個HelloSpeaker類別,讓其實現IHello介面:
- HelloSpeaker.java
package onlyfun.caterpillar;
public class HelloSpeaker implements IHello {
public void hello(String name) {
System.out.println("Hello, " + name);
}
}
現在HelloSpeaker已經撰寫完畢,在不對它進行任何修改的情況下,您想要在hello()方法執行之前,可以記錄一些訊息,想像一下這是您拿到 的一個組件,您沒有原始碼,但您想對它增加一些日誌的服務。您可以先實作MethodBeforeAdvice介面,例如:
- LogBeforeAdvice.java
package onlyfun.caterpillar;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.aop.MethodBeforeAdvice;
public class LogBeforeAdvice
implements MethodBeforeAdvice {
private Logger logger =
Logger.getLogger(this.getClass().getName());
public void before(Method method, Object[] args,
Object target) throws Throwable {
logger.log(Level.INFO,
"method starts..." + method);
}
}
在before()方法的實作中,您加入了一些記錄資訊的程式碼,LogBeforeAdvice類別被設計為一個獨立的服務,可以重複提供服務給需要的物件,接著您只要在定義檔中如下定義:
- 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="logBeforeAdvice"
class="onlyfun.caterpillar.LogBeforeAdvice"/>
<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>logBeforeAdvice</value>
</list>
</property>
</bean>
</beans>
注意到除了建立Advice及Target的物件實例之外,您還使用了 org.springframework.aop.framework.ProxyFactoryBean,這個類別會被BeanFactory或是 ApplicationContext用來建立代理物件(回憶一下前一個小節,Spring AOP主要是透過代理機制來實現,因而需要建立代理物件),您要在"proxyInterfaces"屬性上告知代理時的可運用的介面,在 "target"上告知 Target物件,在"interceptorNames"上告知所要應用的Advice實例,在不指定目標方法時,Before Advice會被縫合(Weave)至介面上所有定義的方法之前。
可以撰寫以下的程式測試一下Before Advice的運作:
- SpringAOPDemo.java
package onlyfun.caterpillar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.
support.FileSystemXmlApplicationContext;
public class SpringAOPDemo {
public static void main(String[] args) {
ApplicationContext context =
new FileSystemXmlApplicationContext(
"beans-config.xml");
IHello helloProxy =
(IHello) context.getBean("helloProxy");
helloProxy.hello("Justin");
}
}
記得在操作取回的代理物件時,必須轉換操作介面為IHello介面,執行結果將會在呼叫hello()方法前進行日誌動作。
您所設計的HelloSpeaker與LogBeforeAdvice是兩個獨立的物件,對於HelloSpeaker來說,它不用知道 LogBeforeAdvice的存在(也就是沒有任何與LogBeforeAdvice相關的API撰寫在HelloSpeaker中),而 LogBeforeAdvice也可以運用至其它的物件之上,HelloSpeaker與LogBeforeAdvice都是可以重複使用的設計。
可以看出AOP的精神,著重於Aspects的辨識,設計可重複使用的Advices,就如OOP重視物件的辨識,設計可重複使用的物件。