淺析Spring.net中的Aop使用
一. 什么是AOP
AOP為Aspect Oriented Programming的縮寫(xiě),意為:面向切面編程(也叫面向方面),可以通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加 功能的一種技術(shù)。AOP實(shí)際是GoF設(shè)計(jì)模式的延續(xù),設(shè)計(jì)模式孜孜不倦追求的是調(diào)用者和被調(diào)用者之間的解耦,AOP可以說(shuō)也是這種目標(biāo)的一種實(shí)現(xiàn)。
主要的功能是:日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理等等。
主要的意圖是:將日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理等代碼從業(yè)務(wù)邏輯代碼中劃分出來(lái),通過(guò)對(duì)這些行為的分離,我們希望可以將它們獨(dú)立到非指導(dǎo)業(yè)務(wù)邏輯的方法中,進(jìn)而改變這些行為的時(shí)候不影響業(yè)務(wù)邏輯的代碼。
可以通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)在不修改源代碼的情況下給程序動(dòng)態(tài)統(tǒng)一添加功能的一種技術(shù)。AOP實(shí)際是GoF設(shè)計(jì)模式的延續(xù),設(shè)計(jì)模式孜孜不倦追求的是調(diào)用者和被調(diào)用者之間的解耦,AOP可以說(shuō)也是這種目標(biāo)的一種實(shí)現(xiàn)。
在Spring中提供了面向切面編程的豐富支持,允許通過(guò)分離應(yīng)用的業(yè)務(wù)邏輯與系統(tǒng)級(jí)服務(wù)(例 如審計(jì)(auditing)和事務(wù)(transaction)管理)進(jìn)行內(nèi)聚性的開(kāi)發(fā)。應(yīng)用對(duì)象只實(shí)現(xiàn)它們應(yīng)該做的——完成業(yè)務(wù)邏輯——僅此而已。它們并 不負(fù)責(zé)(甚至是意識(shí))其它的系統(tǒng)級(jí)關(guān)注點(diǎn),例如日志或事務(wù)支持。
切面(Aspect)
切面是你要實(shí)現(xiàn)的交叉功能。它是應(yīng)用系統(tǒng)模塊化的一個(gè)切面或領(lǐng)域。切面的最常見(jiàn)(雖然簡(jiǎn)單)例子是日志記錄。日志記錄在系統(tǒng)中到處需要用到,利用繼承來(lái)重用日志模塊不適合。然而,你可以創(chuàng)建一個(gè)日志記錄切面,并且使用AOP在系統(tǒng)中應(yīng)用。
連接點(diǎn)(Joinpoint)
連接點(diǎn)是應(yīng)用程序執(zhí)行過(guò)程中插入切面的地點(diǎn)。這個(gè)地點(diǎn)可以是方法調(diào)用,異常拋出,或者甚至是要修改的字段。切面代碼在這些地方插入到你的應(yīng)用流程中,添加新的行為。
通知(Advice)
通知切面的實(shí)際實(shí)現(xiàn)。它通知應(yīng)用系統(tǒng)新的行為。在日志例子中,日志通知包含了實(shí)現(xiàn)實(shí)際日志功能的代碼,如向日志文件寫(xiě)日志。通知在連接點(diǎn)插入到應(yīng)用系統(tǒng)中。
切入點(diǎn)(Pointcut)
切入點(diǎn)定義了通知應(yīng)該應(yīng)用在哪些連接點(diǎn)。通知可以應(yīng)用到AOP框架支持的任何連接點(diǎn)。當(dāng)然,你并不希望把所有切面應(yīng)用到所有可能的連接點(diǎn)上。切入點(diǎn)讓你指定通知應(yīng)用到什么地方。通常通過(guò)指定類(lèi)名和方法名,或者匹配類(lèi)名和方法名式樣的正則表達(dá)式來(lái)指定切入點(diǎn)。一些AOP框架允許動(dòng)態(tài)創(chuàng)建切入點(diǎn),在運(yùn)行時(shí)根據(jù)條件決定是否應(yīng)用切面,如方法參數(shù)值。
引入(Introduction)
引入允許你為已存在類(lèi)添加新方法和屬性。例如,你可以創(chuàng)建一個(gè)稽查通知來(lái)記錄對(duì)象的***修改時(shí)間。只要用一個(gè)方法setLastMofified(Date)以及一個(gè)保存這個(gè)狀態(tài)的變量。可以在不改變已存在類(lèi)的情況下將這個(gè)引入,給他們新的行為和狀態(tài)。
目標(biāo)對(duì)象(Target)
目標(biāo)對(duì)象是被通知對(duì)象。它既可以是你編寫(xiě)的類(lèi)也可以是你要添加制定行為的第三方類(lèi)。如果沒(méi)有AOP,這個(gè)類(lèi)就必須要包含它的主要邏輯以及其他交叉業(yè)務(wù)邏輯。有了AOP,目標(biāo)對(duì)象就可以全身心地關(guān)注主要業(yè)務(wù),忘記應(yīng)用其上的通知。
代理(Proxy)
代理是將通知應(yīng)用到目標(biāo)對(duì)象后創(chuàng)建的對(duì)象。對(duì)于客戶對(duì)象來(lái)說(shuō),目標(biāo)對(duì)象(應(yīng)用AOP之前的對(duì)象)和代理對(duì)象(應(yīng)用AOP之后的對(duì)象)是一樣的。也就是,應(yīng)用系統(tǒng)的其他部分不用為了支持代理對(duì)象而改變。
織入 (Weaving)
織入是將切面應(yīng)用到目標(biāo)對(duì)象從而創(chuàng)建一個(gè)新的代理對(duì)象的過(guò)程。切面在指定接入點(diǎn)被織入到目標(biāo)對(duì)象中。
二. 幾個(gè)重要接口
用于找到用于通知的相關(guān)的類(lèi)型和方法
- View Code
- public interface IPointcut
- {
- ITypeFilter TypeFilter { get; }
- IMethodMatcher MethodMatcher { get; }
- }
用于匹配相關(guān)類(lèi)型
- View Code
- public interface ITypeFilter
- {
- bool Matches(Type type);
- }
- public interface IMethodMatcher
- {
- bool IsRuntime { get; }
- bool Matches(MethodInfo method, Type targetType);
- bool Matches(MethodInfo method, Type targetType, object[] args);
- }
Matches(MethodInfo, Type)方法用來(lái)測(cè)試這個(gè)切入點(diǎn)是否匹配目標(biāo)類(lèi)的指定方法。這將在AOP代理被創(chuàng)建的時(shí)候執(zhí)行,這樣可以避免在每次方法調(diào)用的時(shí)候都執(zhí)行。如果兩個(gè)參數(shù)的matches方法對(duì)于一個(gè)給定的方法返回true,并且IMethodMatcher接口的IsRuntime方法也返回true,那么有三個(gè)參數(shù)的matches方法將在每個(gè)方法調(diào)用時(shí)被調(diào)用. 這使得切入點(diǎn)在通知將被執(zhí)行前可以查看傳入到方法的參數(shù)。
絕大多數(shù)的IMethodMatcher接口是static的,這也就意味著它們的IsRuntime屬性返回false.在這種情況下,有三個(gè)參數(shù)的Matches方法將永遠(yuǎn)不會(huì)被調(diào)用.
三. 通知
3.1 通知生命周期
3.2 通知類(lèi)型
一、攔截環(huán)繞通知(around advice):Spring.NET中最基本的通知類(lèi)型是攔截環(huán)繞通知(interception around advice),即方法攔截器。攔截環(huán)繞通知繼承IMethodInterceptor接口。注意其中IMethodInvocation.Proceed()方法的調(diào)用。該方法會(huì)依次調(diào)用攔截器鏈上的其它攔截器。大部分?jǐn)r截器都需要調(diào)用這個(gè)方法并返回它的返回值。當(dāng)然,也可以不調(diào)用Proceed方法,而返回一個(gè)其它值或拋出一個(gè)異常,但一般不太會(huì)這么做。
二、前置通知(before advise):是在IMethodInterceptor.Proceed()方法調(diào)用前的通知。繼承自IMethodBeforeAdvice接口。
三、異常通知(throws advise):是在IMethodInterceptor.Proceed()方法調(diào)用時(shí)發(fā)生異常的通知。繼承自IthrowsAdvice接口。IthrowsAdvice接口沒(méi)有定義任何方法:它是一個(gè)標(biāo)識(shí)接口(按:之所以用標(biāo)識(shí)接口,原因有二:1、在通知方法中,只有***一個(gè)參數(shù)是必須的。如果聲明為接口的方法,參數(shù)列表就被固定了。2、如果***個(gè)原因可以用重載的接口方法解決,那么這個(gè)原因就是使用標(biāo)識(shí)接口的充分原因了:實(shí)現(xiàn)此接口的類(lèi)必須聲明一或多個(gè)通知方法,接口方法做不到這一點(diǎn)),用以表明實(shí)現(xiàn)它的類(lèi)聲明了一或多個(gè)強(qiáng)類(lèi)型的異常通知方法。
四、后置通知(after returning advise):是在IMethodInterceptor.Proceed()方法調(diào)用后的通知。繼承自IAfterReturningAdvice接口。后置通知對(duì)切入點(diǎn)的執(zhí)行沒(méi)有影響,如果通知拋出異常,就會(huì)沿?cái)r截器鏈向上拋出,從而中斷攔截器鏈的繼續(xù)執(zhí)行。
3.2.1 Interception Around Advice(環(huán)繞攔截通知,后面的講解以環(huán)繞攔截通知為例子)
方法攔截器接口
- View Code
- public interface IMethodInterceptor : IInterceptor
- {
- object Invoke(IMethodInvocation invocation);
- }
模擬環(huán)繞攔截通知
- View Code
- public class DebugInterceptor : IMethodInterceptor
- {
- public object Invoke(IMethodInvocation invocation)
- {
- Console.WriteLine("Before: invocation=[{0}]", invocation);
- object rval = invocation.Proceed();
- Console.WriteLine("Invocation returned");
- return rval;
- }
- }
注意 IMethodInvocation 的 Proceed 方法,proceed方法返回方法的返回值.
四. 切入點(diǎn)操作
4.1 靜態(tài)切入點(diǎn)
4.1.1 ProxyFactoryObject 顯式創(chuàng)建AOP代理
- AOP 配置文件
- <object id="UserValidateTarget" type="Stephen.SpringNet.AOPSample.Servcies.Impl.UserValidate, Servcies"></object>
- <object id="RoundInterceptor" type="Stephen.SpringNet.AOPSample.Servcies.Interceptor.RoundInterceptor, Servcies"></object>
- <object id="UserValidateProxy" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
- <property name="proxyInterfaces" value="Stephen.SpringNet.AOPSample.Servcies.IValidateService"/>
- <property name="target" ref="UserValidateTarget"/>
- <property name="interceptorNames">
- <list>
- <value>RoundInterceptor</value>
- </list>
- </property>
- </object>
不過(guò)由于顯式的創(chuàng)建AOP在要?jiǎng)?chuàng)建多個(gè)代理的時(shí)候需要重復(fù)的配置因此Spring提供了自動(dòng)代理
4.1.2 ObjectNameAutoProxyCreator 對(duì)象名稱(chēng)自動(dòng)切入點(diǎn)
可以用特定的文本值或通配符匹配目標(biāo)對(duì)象的名稱(chēng),并為滿足條件的目標(biāo)對(duì)象創(chuàng)建AOP代理。該類(lèi)支持模式匹配字符串,如:"*name","name*",”*name*“和精確文本如"name"。我們可以通過(guò)下面這個(gè)簡(jiǎn)單的例子了解一下自動(dòng)代理的功能。
- AOP 配置文件
- <object id="IValidateProxy" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
- <property name="ObjectNames">
- <list>
- <value>*Validate</value>
- </list>
- </property>
- <property name="InterceptorNames" value="RoundInterceptor"></property>
- </object>
- 測(cè)試方法
- [TestMethod]
- public void ObjectNameAutoProxyCreatorMethodTest()
- {
- var context = ContextRegistry.GetContext();
- IDictionary validates = context.GetObjectsOfType(typeof(IValidateService));
- foreach (DictionaryEntry validate in validates)
- {
- ((IValidateService) validate.Value).Validate(null);
- }
- }
執(zhí)行結(jié)果
ObjectNameAutoProxyCreatorMethodTest : Passed
Method:Validate開(kāi)始執(zhí)行
Method:Validate執(zhí)行完畢
4.1.3 SdkRegularExpressionMethodPointcut 通過(guò)正則表達(dá)式來(lái)匹配需要執(zhí)行的類(lèi)或方法
- AOP 配置
- <object id="ValidatePointCut" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop">
- <property name="Pattern" value="UserValidate.Advance*"></property>
- </object>
- <aop:config>
- <aop:advisor advice-ref="RoundInterceptor" pointcut-ref="ValidatePointCut"/>
- </aop:config>
pattern 的屬性 UserValidate.Advance* 表示 匹配 UserValidate 類(lèi)中的以Advance開(kāi)頭的方法
4.1.4 DefaultAdvisorAutoProxyCreator+RegularExpressionMethodPointcutAdvisor 創(chuàng)建正則表達(dá)式AOP
- AOP 配置
- <object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"/> <object id="ValidateRegularExpressionPointCut" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
- <property name="advice" ref="RoundInterceptor"/>
- <property name="patterns">
- <list>
- <value>UserValidate.Advance*</value>
- </list>
- </property>
- </object>
4.1.5 特性Aop
可以通過(guò)Attribute類(lèi)來(lái)實(shí)現(xiàn)AOP
- [AttributeUsage(AttributeTargets.Method)]
- public class AopAttribute:Attribute
- {
- }
- AOP 配置
- <object id="aroundAdvisor" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
- <property name="Advice" ref="RoundInterceptor"/>
- <property name="Attribute"
- value ="Stephen.SpringNet.AOPSample.Servcies.AopAttribute, Servcies" />
- </object>
- <object id="ValidateAttributeProxy" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
- <property name="proxyInterfaces" value="Stephen.SpringNet.AOPSample.Servcies.IValidateService"/>
- <property name="target" ref="UserValidateTarget"/>
- <property name="interceptorNames">
- <list>
- <value>aroundAdvisor</value>
- </list>
- </property>
- </object>
原文鏈接:http://www.cnblogs.com/vipyoumay/archive/2012/07/07/2580420.html