1. 程式人生 > >Spring-Aop攔截(轉)

Spring-Aop攔截(轉)

前言:

前面介紹了Spring的核心模組以及相關的依賴注入等概念。這篇講解一下spring的另一個重點,AOP面向切面程式設計。

  說道AOP不得不提到幾個概念:

  切面:也就是我們自己的一些業務方法。

  通知:用於攔截時出發的操作。

  切點:具體攔截的某個業務點。

  這樣說可能還是有點抽象,舉個例子,下面是一個紙糊的多面體。

  每個面都是一個業務方法,我們通過刺穿每一個面,都可以進入到內部,這個面就是一個切面

  刺穿的時候會發出聲響,這就是一種通知

  而具體從哪個面刺入,這就是一個切入點的選擇了。

  這樣說,應該能稍微瞭解一點。

  那麼下面看一個簡單的例子:

  為了便於理清關係,先放上一張相關的類圖:

  首先定義個介面

1 public interface IService {
2  public void withAop();
3  public void withoutAop();
4 }

  有了介面,當然需要一個實現類

複製程式碼
 1 public class TestAOP implements IService {
 2  private String name;
 3  public void withAop() {
 4   System.out.println("with AOP name:"+name);
 5  }
 6  public void withoutAop() {
 7
System.out.println("without AOP name:"+name); 8 } 9 public String getName() { 10 return name; 11 } 12 public void setName(String name) { 13 this.name = name; 14 } 15 }
複製程式碼

  這個實現類實現了介面定義的兩個方法,下面我們定義幾種攔截方式,這些攔截方式通過攔截的位置或者時機不同而不同。

  通常有方法前攔截方法後攔截,以及異常攔截。通過在這些攔截中編寫自己的業務處理,可以達到特定的需求。

  方法前攔截,需要實現MethodBeforeAdvice介面,並填寫before方法。這樣,當攔截到某個方法時,就會在方法執行前執行這個before()方法。

1 public class BeforeAOPInterceptor implements MethodBeforeAdvice{
2  public void before(Method method, Object[] args, Object instance)
3    throws Throwable {
4   System.out.println("before()"+method.getName());
5  }
6 }

  同理,方法後攔截,也是如此。需要實現AfterReturningAdvice介面。

1 public class AfterAOPInterceptor implements AfterReturningAdvice{
2  public void afterReturning(Object value, Method method, Object[] args,
3    Object instance) throws Throwable {
4   System.out.println("after()"+method.getName());
5  }
6 }

  以及異常攔截。

複製程式碼
1 public class ThrowsAOPInterceptor implements ThrowsAdvice{ 
2  public void afterThrowing(Method method,Object[] args,Object instance,AccountException ex) throws Throwable{
3   System.out.println("after()"+method.getName()+"throws exception:"+ex);
4  }
5  public void afterThrowing(NullPointerException ex) throws Throwable{
6   System.out.println("throws exception:"+ex);
7  }
8 }
複製程式碼

  接下來就需要配置一下spring的配置檔案,把攔截器與切面方法關聯起來。

  參考上面的圖,可以看到配置檔案中的層次關係。

複製程式碼
 1 <?xml version="1.0" encoding="UTF-8"?> 
 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 3 xmlns="http://www.springframework.org/schema/beans" 
 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 5 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 
 6 <!-- 通過名字匹配 --> 
 7 <!-- 
 8   <bean id="before" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> 
 9     <property name="advice"> 
10       <bean class="com.test.pointcut.beforeAOP"></bean> 
11     </property> 
12     <property name="mappedName" value="withoutAop"></property> 
13   </bean> 
14 --> 
15 <!-- 通過正則表示式 匹配 --> 
16   <bean id="before" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> 
17     <property name="advice"> 
18       <bean class="com.test.pointcut.BeforeAOPInterceptor"></bean> 
19     </property> 
20   <property name="patterns"> 
21     <list> 
22       <value>.*out.*</value> 
23     </list> 
24   </property> 
25   </bean> 
26   <bean id="after" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> 
27     <property name="advice"> 
28       <bean class="com.test.pointcut.AfterAOPInterceptor"></bean> 
29     </property> 
30     <property name="patterns"> 
31       <list> 
32         <value>.*out.*</value> 
33       </list> 
34     </property> 
35   </bean> 
36   <bean id="exception" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> 
37     <property name="advice"> 
38       <bean class="com.test.pointcut.ThrowsAOPInterceptor"></bean> 
39     </property> 
40     <property name="patterns"> 
41       <list> 
42         <value>.*out.*</value> 
43       </list> 
44     </property> 
45   </bean> 
46 <!-- --> 
47   <bean id="aopService" class="org.springframework.aop.framework.ProxyFactoryBean"> 
48     <property name="interceptorNames"> 
49       <list> 
50         <value>before</value> 
51         <value>after</value> 
52         <value>exception</value> 
53       </list> 
54     </property> 
55     <property name="target"> 
56       <bean class="com.test.pointcut.TestAOP"> 
57         <property name="name" value="Hello"></property> 
58       </bean> 
59     </property> 
60   </bean> 
61 </beans>
複製程式碼

  ProxyFactoryBean下有兩個屬性,一個想要攔截的目標類,一個是攔截器。而攔截器又包括兩種,主要是因為定位方法的不同而分類。分別是:

  RegexpMethodPointcutAdvisor 通過正則表示式來定位業務方法。

  NameMatchMethodPointcutAdvisor 通過名字來定位業務方法。

  定位到了業務方法,還需要新增響應的攔截器,攔截器就是上面的三種。

  最後看一下測試的方法:

複製程式碼
public class TestMain {
 public static void main(String[] args) {
  XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContextAOP.xml"));
  IService hello = (IService)factory.getBean("aopService");
  hello.withAop();
  hello.withoutAop();
 }
}
複製程式碼

  我們上面通過正則表示式定位到所有包含out的方法,其實就是withoutAOP方法。這樣當執行withoutAop方法時,會觸發攔截器的操作。

  執行結果:

2014-12-4 16:46:58 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
資訊: Loading XML bean definitions from class path resource [applicationContextAOP.xml]
with AOP name:Hello
before()withoutAop
without AOP name:Hello
after()withoutAop

  總結:

  這是通過定義切入點的方式來實現AOP,通過這種程式設計方式,可以針對業務方法進行包裝或者監控

  舉個例子,比如有個業務方法想要進行資料的查詢,那麼可以再這個查詢前面獲取JDBC連線池的連線,這樣就對使用者遮蔽掉了複雜的申請過程。而銷燬就可以放在方法後攔截函式裡。

  再比如,想要監控某個業務方法唄執行了多少次,那麼就可以通過這樣一種攔截方式,進行資訊的統計,計數或者計時!

  妙處多多,還待完善!