在Spring1.2或之前的版本中,實現AOP的傳統方式就是通過實現Spring的AOP API來定義Advice,并設置代理對象。Spring根據Adivce加入到業務流程的時機的不同,提供了四種不同的Advice:Before Advice、After Advice、Around Advice、Throw Advice。
1、Before Advice
顧名思義,Before Advice會在目標對象的方法執行之前被調用,您可以通過實現org.springframework.aop.MethodBeforeAdvice接口來實現Before Advice的邏輯,接口定義如下:
java 代碼

   
1package org.springframework.aop;
   
2.
   
3public interface MethodBeforeAdvice extends BeforeAdvice {
   
4.    void before(Method method, Object[] args, Object target) throws Throwable;
   
5. }


其中BeforeAdvice繼承自Adivce接口,這兩者都是標簽接口,并沒有定義任何具體的方法。before方法會在目標對象的指定方法執行之前被執行,在before方法種,你可以取得指定方法的Method實例、參數列表和目標對象,在before方法執行完后,目標對象上的方法將會執行,除非在before方法種拋出異常。
下面通過例子來說明Before Advice的使用方法。首先定義目標對象所要實現的接口:
java 代碼

   
1package com.savage.aop
   
2.
   
3public interface MessageSender {
   
4.    void send(String message);
   
5. }


接著實現MessageSender接口:
java 代碼

   
1package com.savage.aop;
   
2.
   
3public class HttpMessageSender implements MessageSender {
   
4public void send(String message) {
   
5. System.out.println("Send Message[" + message + "] by http.");
   
6. }
   
7. }


OK,我們的業務代碼實現完了,現在如果要在不改變我們的業務代碼的前提下,在執行業務代碼前要記錄一些日志,這時就可以通過實現MethodBeforeAdvice接口來實現,如:
java 代碼

   
1package com.savage.aop;
   
2.
   
3import java.lang.reflect.Method;
   
4.
   
5import org.springframework.aop.framework.MethodBeforeAdvice;
   
6.
   
7public class LogBeforeAdvice implements MethodAdvice {
   
8.    public void before(Method method, Object[] args, Object target) throws Throwable {
   
9.       System.out.println("Log before " + method + " by LogBeforeAdvice.");
  
10.    }
  
11. }


然后再在XML進行如下定義:
xml 代碼
 

   
1<?xml version="1.0" encoding="UTF-8"?>    
   
2<beans xmlns="http://www.springframework.org/schema/beans"    
   
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
   
4. xsi:schemaLocation="http://www.springframework.org/schema/beans    
   5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">    
   6.   
   
7.     <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>    
   
8.       
   
9.     <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean>    
  
10.       
  
11.     <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">    
  
12.         <property name="proxyInterfaces" value="com.savage.aop.MessageSender"/>    
  
13.         <property name="target" ref="messageSenderImpl"/>    
  
14.         <property name="interceptorNames">    
  
15.             <list>    
  
16.                 <value>logBeforeAdvice</value>    
  
17.             </list>    
  
18.         </property>    
  
19.     </bean>    
  
20</beans>   



這樣我們就為MessageSender對象指定了Before Advice對象。在這里,我們分別定義了一個MessageSender對象(messageSenderImpl)和一個Before Advice對象(logBeforeAdvice),并定義了一個 org.springframework.aop.framework.ProxyFactoryBean對象(messageSender),FactoryBean或ApplicationContext將使用ProxyFactoryBean來建立代理對象,在這里就是messageSenderImpl建立代理對象。在ProxyFactoryBean的定義中,proxyInterfaces屬性指定了要代理的接口;target指定了要建立代理的目標對象;interceptorNames則指定了應用與指定接口上的Advices對象列表,spring將根據列表中定義的順序在執行目標對象的方法前、后執行Advice中定義的方法。
現在我們寫一個程序來驗證下:
java 代碼

   
1package com.savage.aop;
   
2.
   
3import org.springframework.context.ApplicationContext;
   
4import org.springframework.context.support.ClassPathXmlApplication;
   
5.
   
6public class AdviceDemo {
   
7.    public void main(String[] args) {
   
8.       ApplicationContext context = new ClassPathXmlApplicationContext("beans-config.xml");
   
9.       MessageSender sender = (MessageSender)context.getBean("messageSender");
  
10.       sender.send("message");
  
11.   }
  
12. }


執行結果:
Log before 
public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogBeforeAdvice.
Send Message[message] by http.
正如你所看到的,在執行MessageSender的send方法前先執行了LogBeforeAdvice的方法!在這個例子中,記錄日志的代碼并沒有橫切到我們的業務代碼中,LogBeforeAdvice和HttpMessageSender彼此不知道對方的存在,而且我們的應用程序AdviceDemo對LogBeforeAdvice的存在也是一無所知。假如有一天我們的應用程序不需要再業務代碼執行前記錄日志了,只需要修改XML文件中的定義,而不用更改AdviceDemo的代碼:
xml 代碼

   
1<bean id="messageSender" class="com.savage.aop.HttpMessageSender">bean>


2、After Advice
After Advice會在目標對象的方法執行完后執行,你可以通過實現org.springframework.aop.AfterReturingAdvice接口來實現After Advice的邏輯,AfterReturingAdvice接口定義如下:
java 代碼

   
1package org.springframework.aop;
   
2.
   
3public interface AfterReturningAdvice {
   
4.    void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
   
5. }


在afterReturning方法中,你可以獲得目標方法執行后的返回值、目標方法對象、目標方法的參數以及目標對象。
繼續以上面的例子為例,如果要在MessageSender的send方法執行完后,要再記錄日志,那么我們可以先實現AfterReturningAdvice接口:
java 代碼

   
1package com.savage.aop;
   
2.
   
3import org.springframework.aop;
   
4.
   
5public LogAfterAdvice implements AfterReturningAdvice {
   
6.    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
   
7.       System.out.println("Log after " + method + " by LogAfterAdvice.");
   
8.    }
   
9. }


然后在XML文件中指定LogAfterAdvice的實例:
xml 代碼
 

   
1<?xml version="1.0" encoding="UTF-8"?>    
   
2<beans xmlns="http://www.springframework.org/schema/beans"    
   
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
   
4. xsi:schemaLocation="http://www.springframework.org/schema/beans    
   5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">    
   6.   
   
7.     <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>    
   
8.       
   
9.     <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean>    
  
10.       
  
11.     <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">    
  
12.         <property name="proxyInterfaces" value="com.savage.aop.MessageSender"/>    
  
13.         <property name="target" ref="messageSenderImpl"/>    
  
14.         <property name="interceptorNames">    
  
15.             <list>     
  
16.                 <value>logAfterAdvice</value>  
  
17.             </list>    
  
18.         </property>    
  
19.     </bean>    
  
20</beans>   



在前面Before Advice的基礎上,我們為MessageSender再指定了一個LogAfterAdvice的服務。運行前面的AdviceDemo,結果如下:
Send Message[message] by http.
Log after 
public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAfterAdvice.

3、Around Advice
在上面的LogAfterAdvice例子中,我們通過指定BeforeAdvice和AfterReturingAdvice,在MessageSender的send方法前后執行額外的業務。實際上,如果需要在業務代碼執行前后增加額外的服務,你可以直接通過實現org.aopalliance.intercept.MethodInterceptor接口來達到這一目的,MethodInterceptor定義如下:
java 代碼

   
1package org.aopalliance.intercept;
   
2.
   
3public interface MethodInterceptor {
   
4.    public Object invoke(MethodInvocation methodInvocation) throws Throwable;
   
5. }


例如:
java 代碼

   
1package com.savage.aop;
   
2.
   
3import org.aopalliance.intercept.MethodInterceptor;
   
4import org.aopalliance.intercept.MethodInvocation;
   
5.
   
6public class LogAdvice implements MethodInterceptor {
   
7.    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
   
8.       System.out.println("Log before " + methodInvocation.getMethod() + " by LogAdvice.");
   
9.       Object retValue = methodInvocation.proceed();
  
10.       System.out.println("Log after " + methodInvocation.getMethod() + " by LogAdvice.");
  
11.       return retValue;
  
12.    }
  
13. }


正如上面所示,在MethodInterceptor中你得自行決定是否調用MethodInvocation的proceed()方法來執行目標對象上的方法,proceed()方法在執行完后會返回目標對象上方法的執行結果。
MethodInterceptor在XML文件中的定義如下:
xml 代碼
 

   
1<?xml version="1.0" encoding="UTF-8"?>    
   
2<beans xmlns="http://www.springframework.org/schema/beans"    
   
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
   
4. xsi:schemaLocation="http://www.springframework.org/schema/beans    
   5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">    
   6.   
   
7.     <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>    
   
8.       
   
9.     <bean id="logAdvice" class="com.savage.aop.LogAdvice"></bean>    
  
10.       
  
11.     <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">  
  
12.         <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/>    
  
13.         <property id="target" ref="messageSenderImpl"/>  
  
14.         <property id="interceptorNames">  
  
15.             <list>  
  
16.                 <value>logAdvice</value>  
  
17.             </list>  
  
18.         </property>  
  
19.     </bean>  
  
20</beans>  



Spring在真正執行目標對象的方法前,會執行interceptorNames中執行的Advice,每個Advice在執行完自己的業務后,會調用MethodInvocation的proceed()方法,將執行的主動權移交給下一個Advice,直到沒有下一個Advice為止,在執行完目標對象的方法后,Spring會再以相反的順序一層層的返回。例如:
xml 代碼
 

   
1<bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">  
   
2.     <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/>    
   
3.     <property id="target" ref="messageSenderImpl"/>  
   
4.     <property id="interceptorNames">  
   
5.         <list>  
   
6.             <value>logBeforeAdvice</value>  
   
7.             <value>logAdvice</value>  
   
8.             <value>logAfterAdvice</value>  
   
9.         </list>  
  
10.     </property>  
  
11</bean>  



象上面這個例子,logBeforeAdvice先會被執行,然后執行logAdvice,接著執行logAfterAdvice,最后又返回到了logAdvice。
現在我們把LogAdvice作一下簡單的修改,增加一個id屬性,用以在后面查看Advice的調用順序:
java 代碼

   
1package com.savage.aop;
   
2.
   
3import org.aopalliance.intercept.MethodInterceptor;
   
4import org.aopalliance.intercept.MethodInvocation;
   
5.
   
6public class LogAdvice implements MethodInterceptor {
   
7.    private static int INSTANCE_NUM = 0;
   
8.
   
9.    private int id;
  
10.
  
11.    public LogAdvice() {
  
12.       id = ++INSTANCE_NUM;
  
13.    }
  
14.
  
15.    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  
16.       System.out.println("Log before " + methodInvocation.getMethod() + " by LogAdvice[" + id + "].");
  
17.       Object retValue = methodInvocation.proceed();
  
18.       System.out.println("Log after " + methodInvocation.getMethod() + " by LogAdvice[" + id + "].");
  
19.       return retValue;
  
20.    }
  
21. }


同時把XML中的定義改為:
xml 代碼
 

   
1<?xml version="1.0" encoding="UTF-8"?>    
   
2<beans xmlns="http://www.springframework.org/schema/beans"    
   
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
   
4. xsi:schemaLocation="http://www.springframework.org/schema/beans    
   5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">    
   6.   
   
7.     <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean>    
   
8.       
   
9.     <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean>    
  
10.     <bean id="logAfterAdvice" class="com.savage.aop.LogAfterAdvice"></bean>    
  
11.     <bean id="logAdvice1" class="com.savage.aop.LogAdvice"></bean>    
  
12.     <bean id="logAdvice2" class="com.savage.aop.LogAdvice"></bean>    
  
13.       
  
14.     <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean">  
  
15.         <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/>    
  
16.         <property id="target" ref="messageSenderImpl"/>  
  
17.         <property id="interceptorNames">  
  
18.             <list>  
  
19.                 <value>logBeforeAdvice</value>  
  
20.                 <value>logAdvice1</value>  
  
21.                 <value>logAfterAdvice</value>  
  
22.                 <value>logAdvice2</value>  
  
23.             </list>  
  
24.         </property>  
  
25.     </bean>  
  
26</beans>  



現在再執行AdviceDemo,得到如下結果:
Log before 
public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogBeforeAdvice.
Log before 
public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAdvice[1].
Log before 
public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAdvice[2].
Send Message[message] by http.
Log after 
public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAdvice[2].
Log after 
public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAfterAdvice.
Log after 
public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAdvice[1].

4、Throw Advice
如果想要在異常發生時執行某些業務,你可以通過實現org.springframework.aop.ThrowsAdvice接口,這是一個標簽接口,沒有定義任何方法,你可以在當中為每個你需要處理的異常類定義afterThrowing方法,當程序出現異常時,spring會根據異常的類型調用對應的afterThrowing方法。AfterThrowing的格式如下:
java 代碼

   
1. afterThrowing([Method],[args],[target],subClassOfThrowable);


方括號[]中的參數為可選項,但方法中必須有subClassOfThrowable,且必須是Throwable的子類。
Spring在調用完afterThrowing方法后,原先的異常會繼續在程序中傳播,如果象要終止程序對異常的處理,只能在afterThrowing方法中拋出其他異常。