Spring 面向方面程式設計 AOP
Spring AOP(面向方面程式設計)框架,用於在模組化方面的橫切關注點。簡單得說,它只是一個攔截器,用來攔截一些過程。例如,當一個方法執行,Spring AOP 可以劫持此執行方法,在方法執行之前或之後新增額外的功能。
在Spring AOP中,有 4 種類型通知(advices)的支援: 之前通知 - 該方法執行前執行 -MethodBeforeAdvice 返回後通知 – 執行後,該方法返回一個結果 丟擲之後通知 – 執行方法丟擲異常後, 環繞通知 – 環繞方法執行執行,結合以上這三個通知。
1. 之前通知
它會在方法執行之前執行。建立一個實現 MethodBeforeAdvice 介面的類。
public class ArticleServiceMethodBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("Before " + method.getName() + " of " + target.getClass().getSimpleName() + " class.");
}
}
在 bean 配置檔案(Spring-Bean.xml)配置代理物件。
<bean id="articleDao" class="com.angelia.spring.dao.ArticleDaoImpl"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="articleService" class="com.angelia.spring.service.ArticleServiceImpl" scope="prototype"> <property name="articleDao"> <ref bean="articleDao" /> </property> </bean> <bean id="articleServiceBeforeAdvice" class="com.angelia.spring.dao.aop.ArticleServiceMethodBeforeAdvice" /> <bean id="articleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="articleService" /> <property name="interceptorNames"> <list> <value>articleServiceBeforeAdvice</value> </list> </property> </bean>
‘target’ – 定義你想攔截的bean。‘interceptorNames’ – 定義要應用這個代理/目標物件的類(通知)。
2. 返回後通知
該方法返回一個結果之後它將執行。建立一個實現AfterReturningAdvice介面的類。
public class ArticleServiceAfterReturningAdvice implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("After return result of the " + method.getName() + " of " + target.getClass().getSimpleName() + " class.");
}
}
bean配置檔案
<bean id="articleServiceAfterAdvice" class="com.angelia.spring.dao.aop.ArticleServiceAfterReturningAdvice" />
<bean id="articleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="articleService" />
<property name="interceptorNames">
<list>
<value>articleServiceBeforeAdvice</value>
<value>articleServiceAfterAdvice</value>
</list>
</property>
</bean>
3.丟擲後通知
它將在執行方法丟擲一個異常後。建立一個實現ThrowsAdvice介面的類,並建立一個afterThrowing方法攔截丟擲:IllegalArgumentException異常
public class ArticleServiceThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(IllegalArgumentException e) throws Throwable {
System.out.println("ArticleServiceThrowsAdvice : Throw exception in ArticleServiceImpl!");
}
}
bean配置檔案
<bean id="articleServiceThrowsAdvice" class="com.angelia.spring.dao.aop.ArticleServiceThrowsAdvice" />
<bean id="articleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="articleService" />
<property name="interceptorNames">
<list>
<value>articleServiceBeforeAdvice</value>
<value>articleServiceAfterAdvice</value>
<value>articleServiceThrowsAdvice</value>
</list>
</property>
</bean>
4.環繞通知
環繞通知結合了上面的三個通知,在方法執行過程中執行。建立一個實現了MethodInterceptor介面的類。
public class ArticleServiceAroundMethod implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
// MethodBeforeAdvice
System.out.println("ArticleServiceMethodBeforeAdvice...");
try {
// 執行原來的方法
Object result = invocation.proceed();
// AfterReturningAdvice
System.out.println("ArticleServiceAfterReturningAdvice...");
return result;
} catch (IllegalArgumentException e) {
// ThrowsAdvice
System.out.println("ArticleServiceThrowsAdvice...");
throw e;
}
}
}
必須呼叫“invocation.proceed();” 繼續在原來的方法執行,否則原來的方法將不會執行。
bean配置檔案
<bean id="articleServiceAroundMethod" class="com.angelia.spring.dao.aop.ArticleServiceAroundMethod" />
<bean id="articleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="articleService" />
<property name="interceptorNames">
<list>
<!-- <value>articleServiceBeforeAdvice</value>
<value>articleServiceAfterAdvice</value>
<value>articleServiceThrowsAdvice</value> -->
<value>articleServiceAroundMethod</value>
</list>
</property>
</bean>
在每一個 ArticleService 方法執行後,將執行 ArticleServiceAroundMethod 的 invoke() 方法。
5. Spring AOP 切入點 (Pointcut,Advisor)
上面的Spring AOP通知的例子,一個類的整個方法被自動攔截。但在大多數情況下,可能只需要一種方式來攔截部分方法,這就是為什麼引入'切入點'的原因。它允許你根據method的名字去攔截指定的method。另外,一個Pointcut必須結合一個Advisor來使用。
在Spring AOP中,有3個常用的概念,Advices、Pointcut、Advisor。
- Advices:表示一個method執行前或執行後的動作。
- Pointcut:表示根據method的名字或者正則表示式去攔截一個method。
- Advisor:Advice和Pointcut組成的獨立的單元,並且能夠傳給proxy factory 物件。
前面的例子,ArticleServiceImpl中全部的method方法全部被攔截了,下邊將利用Pointcuts只攔截queryArticleById()。我們可以用名字匹配法和正則表示式匹配法去匹配要攔截的method。
切入點 - 名稱匹配
建立一個NameMatchMethodPointcut的bean,將你想攔截的方法的名字queryArticleById注入到屬性mappedName。建立一個DefaultPointcutAdvisor的advisor bean,將pointcut和advice關聯起來。
<bean id="articleServiceBeforeAdvice" class="com.angelia.spring.dao.aop.ArticleServiceMethodBeforeAdvice" />
<bean id="articlePointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedName" value="queryArticleById" />
</bean>
<bean id="articleAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut" ref="articlePointcut" />
<property name="advice" ref="articleServiceBeforeAdvice" />
</bean>
<bean id="articleServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="articleService" />
<property name="interceptorNames">
<list>
<!-- <value>articleServiceBeforeAdvice</value> -->
<value>articleAdvisor</value>
</list>
</property>
</bean>
以上配置中pointcut和advisor可以合併在一起配置,即不用單獨配置articlePointcut和articleAdvisor,只要配置articleAdvisor時class選擇NameMatchMethodPointcutAdvisor如下:
<bean id="articleAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="mappedName" value="queryArticleById" />
<property name="advice" ref="articleServiceBeforeAdvice" />
</bean>
實際上這種做法將 method 名字與具體的advice捆綁在一起,有悖於 Spring 鬆耦合理念,如果將 method 名字單獨配置成pointcut(切入點),advice和pointcut的結合會更靈活,使一個pointcut可以和多個advice結合。
切入點 - 正則表示式
可以用正則表示式匹配需要攔截的method。
<bean id="articleAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="patterns">
<list>
<value>.*Article.*</value>
</list>
</property>
<property name="advice" ref="articleServiceBeforeAdvice" />
</bean>
現在,你可以攔截名字中包含Article字元的method了。
6. Spring自動建立代理
前面的 Spring AOP例子 – advice, pointcut 和 advisor, 必須手動建立一個代理bean(ProxyFactryBean),對每個Bean需要AOP支援。這顯然不是一種有效的方式。例如,如果想在客戶模組,所有的Servic類實現SQL日誌支援的AOP功能,那麼必須手動建立很多代理工廠bean,因此在 bean配置檔案可能會氾濫代理類。幸運的是,Spring有兩個自動代理建立者來自動建立代理bean。
BeanNameAutoProxyCreator
自動代理機制,只需要建立一個的 BeanNameAutoProxyCreator,幷包含所有你的bean(通過bean的名字,或正則表示式名)和“advisor” 作為一個單位。
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>*Service</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>articleAdvisor</value>
</list>
</property>
</bean>
現在,可以通過“articleService”的原始名稱獲取bean, 如果知道這個bean已經代理。
DefaultAdvisorAutoProxyCreator
DefaultAdvisorAutoProxyCreator 是非常強大的,如果有 bean 相關連,Spring會自動建立一個代理。
<bean id="articleServiceBeforeAdvice" class="com.angelia.spring.dao.aop.ArticleServiceMethodBeforeAdvice" />
<bean id="articleAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="mappedName" value="queryArticleById" />
<property name="advice" ref="articleServiceBeforeAdvice" />
</bean>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />