四、SpringAOP
學習AOP必須首先要學習代理模式。詳見我的上一篇筆記:AOP基礎:動態代理
AOP觀念與術語
1.橫切關注點(Cross-cutting concern)
類似于日志記錄、安全檢查、事務等系統層面的服務,在一些應用程序中常被尖刀安插至各個對象的處理流程中,這些動作在AOP術語中被稱為橫切關注點。
2.切面(Aspect)
將散落于各個業務對象中的橫切關注點(如日志記錄)收集起來,設計各個獨立可重用的對象,這些對象被稱為切面。如上篇筆記中的Handler類。在需要該服務時,織入(Weave)應用程序之上。
3.Advice
Aspect的具體實現被稱之為Advice。例如,Advice中會包括日志記錄程序代碼是如何實現的。Advice中包含了橫切關注點的行為或提供的服務。
4.Joinpoint
Aspect在應用程序執行時加入業務流程的點或時機。這個時機可能是某個方法執行之前或之后或兩者都有,或是某個異常發生的時候。
5.Pointcut
Pointcut是一個定義,可在某個定義文件中編寫Pointcut,指定某個Aspect在哪些Joinpoint時被織入。
Spring AOP
Spring的Advice是在執行時期導入至Targets的,可以讓Target實現預先定義的接口,則Spring在執行時會使用動態代理;如不實現接口,則會使用CGLIB為Target產生一個子類作為代理類。
Spring只支持方法的Joinpoints,即Advices將在方法執行的前后調用。
Advices
Advices包括Aspect的真正邏輯,具體說來就是一個類,由于織入至Targets的時機不同,Spring提供了幾種不同的Advices,如BeforeAdvice、AfterAdvice、ArountAdvice、ThrowAdvice。
BeforeAdvice
通過實現org.springframework.aop.MethodBeforeAdvice接口來實現邏輯。
該接口定義如下方法:
public void before(Method method, Object[] args, Object target)
throws Throwable;
Before()方法聲明為void,所以不用回傳任何結果,在before()執行完畢之后,除非拋出異常,否則目標對象上的方法就會被執行。
示例代碼如下:
1
public class LogBeforeAdvice implements MethodBeforeAdvice
{
2
Logger logger = Logger.getLogger(this.getClass().getName());
3
public void before(Method method, Object[] args, Object target)
4
throws Throwable
{
5
logger.log(Level.INFO,"Method starts
");
6
}
7
}
在配置文件中寫入以下內容:
1
<bean id="logBeforeAdvice" class="SpringAOP.LogBeforeAdvice"></bean>
2
3
<bean id="helloSpeakerProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
4
<!-- 如果下一行代碼去掉,Spring將會使用CGLIB創建一個代理類 -->
5
<property name="proxyInterfaces" value="SpringAOP.IHello"></property>
6
<property name="target" ref="helloSpeaker"></property>
7
<property name="interceptorNames">
8
<list>
9
<value>logBeforeAdvice</value>
10
</list>
11
</property>
12
</bean>
AfterAdvice
通過實現org.springframework.aop.AfterReturningAdvice接口來實現。
該接口定義如下方法:
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
示例代碼類似于BeforeAdvice。
AroundAdvice
通過實現org.aopalliance.intercept.MethodIntercept接口來實現。
該接口定義如下方法:
public Object invoke(MethodInvocation methodInvocation) throws Throwable;
示例代碼如下:
1
import org.aopalliance.intercept.MethodInterceptor;
2
import org.aopalliance.intercept.MethodInvocation;
3
4
public class LogAroundAdvice implements MethodInterceptor
{
5
public Object invoke(MethodInvocation methodInvocation) throws Throwable
{
6
System.out.println("AroundAdvice Begin
");
7
Object result = methodInvocation.proceed();
8
System.out.println("AroundAdvice Finish
");
9
return result;
10
}
11
}
配置文件類似于BeforeAdvice。
ThrowAdvice
通過實現org.springframework.aop.ThrowAdvice接口來實現。
該接口只是個標簽接口,沒有任何方法??梢远x任意方法名稱,只要是如下形式:
methodName([Method], [args], [target], subclassOfThrowable);
其中[]表示可選。subclassOfThrowable必須是Throwable的子類。當異常發生時,會檢驗所設定的Throw Advice中是否有符合異常類型的方法,如有則執行。
注意:當異常發生時,ThrowAdvice的任務只是執行對應的方法,并不能處理異常。當其執行完畢后,原來的異常仍被傳播至應用程序之中。
Pointcut與Advisor
在Spring中,可以指定更精細的織入時機,Pointcut定義了Advice的應用時機,在Spring中,使用PointcutAdvisor將Pointcut與Advice結合成一個對象。Spring內建的Pointcut都有對應的PointcutAdvisor。
1
Public interface Advisor
{
2
boolean isPerInstance();
3
Advice getAdvice();
4
}
5
Public interface PointcutAdvisor extends Advisor
{
6
Pointcut getPointcut()
7
}
NameMatchMethodPointcutAdvisor
這是最基本的PointcutAdvisor??梢灾付ㄋ獞玫哪繕松系姆椒Q。
示例代碼:
1
<bean id="helloPointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
2
<property name="mappedName">
3
<value>say*</value>
4
</property>
5
<property name="advice">
6
<ref bean="logAroundAdvice"/>
7
</property>
8
</bean>
9
10
<bean id="helloSpeakerProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
11
<!-- 如果下一行代碼去掉,Spring將會使用CGLIB創建一個代理類 -->
12
<property name="proxyInterfaces" value="SpringAOP.IHello"></property>
13
<property name="target" ref="helloSpeaker"></property>
14
<property name="interceptorNames">
15
<list>
16
<!--
17
<value>logBeforeAdvice</value>
18
<value>logAfterAdvice</value>
19
<value>logAroundAdvice</value>
20
-->
21
<value>helloPointcutAdvisor</value>
22
</list>
23
</property>
24
</bean>
Autoproxy
自動代理可以不用為每一個目標對象手動定義代理對象,使用自動代理,可以透過Bean名稱或是Pointcut的比對,自動為符合比對條件的目標對象建立代理對象。
BeanNameAutoProxyCreator
當應用程序很大時,可以為目標對象取好適當的Bean名稱,例如xxxService。此時可修改Bean定義文件如下:
1
<bean id="proxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
2
<property name="beanNames">
3
<list>
4
<value>*Speaker</value>
5
</list>
6
</property>
7
<property name="interceptorNames">
8
<list>
9
<value>logAroundAdvice</value>
10
</list>
11
</property>
12
</bean>