一
什麼是AOP
AOP為Aspect Oriented Programming的縮寫
意為
面向切面編程(也叫面向方面)
可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一添加 功能的一種技術
AOP實際是GoF設計模式的延續
設計模式孜孜不倦追求的是調用者和被調用者之間的解耦
AOP可以說也是這種目標的一種實現
主要的功能是
日志記錄
性能統計
安全控制
事務處理
異常處理等等
主要的意圖是
將日志記錄
性能統計
安全控制
事務處理
異常處理等代碼從業務邏輯代碼中劃分出來
通過對這些行為的分離
我們希望可以將它們獨立到非指導業務邏輯的方法中
進而改變這些行為的時候不影響業務邏輯的代碼
可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一添加功能的一種技術
AOP實際是GoF設計模式的延續
設計模式孜孜不倦追求的是調用者和被調用者之間的解耦
AOP可以說也是這種目標的一種實現
在Spring中提供了面向切面編程的豐富支持
允許通過分離應用的業務邏輯與系統級服務(例 如審計(auditing)和事務(transaction)管理)進行內聚性的開發
應用對象只實現它們應該做的
完成業務邏輯
僅此而已
它們並 不負責(甚至是意識)其它的系統級關注點
例如日志或事務支持
切面(Aspect)
切面是你要實現的交叉功能
它是應用系統模塊化的一個切面或領域
切面的最常見(雖然簡單)例子是日志記錄
日志記錄在系統中到處需要用到
利用繼承來重用日志模塊不適合
然而
你可以創建一個日志記錄切面
並且使用AOP在系統中應用
連接點(Joinpoint)
連接點是應用程序執行過程中插入切面的地點
這個地點可以是方法調用
異常拋出
或者甚至是要修改的字段
切面代碼在這些地方插入到你的應用流程中
添加新的行為
通知(Advice)
通知切面的實際實現
它通知應用系統新的行為
在日志例子中
日志通知包含了實現實際日志功能的代碼
如向日志文件寫日志
通知在連接點插入到應用系統中
切入點(Pointcut)
切入點定義了通知應該應用在哪些連接點
通知可以應用到AOP框架支持的任何連接點
當然
你並不希望把所有切面應用到所有可能的連接點上
切入點讓你指定通知應用到什麼地方
通常通過指定類名和方法名
或者匹配類名和方法名式樣的正則表達式來指定切入點
一些AOP框架允許動態創建切入點
在運行時根據條件決定是否應用切面
如方法參數值
引入(Introduction)
引入允許你為已存在類添加新方法和屬性
例如
你可以創建一個稽查通知來記錄對象的最後修改時間
只要用一個方法setLastMofified(Date)以及一個保存這個狀態的變量
可以在不改變已存在類的情況下將這個引入
給他們新的行為和狀態
目標對象(Target)
目標對象是被通知對象
它既可以是你編寫的類也可以是你要添加制定行為的第三方類
如果沒有AOP
這個類就必須要包含它的主要邏輯以及其他交叉業務邏輯
有了AOP
目標對象就可以全身心地關注主要業務
忘記應用其上的通知
代理(Proxy)
代理是將通知應用到目標對象後創建的對象
對於客戶對象來說
目標對象(應用AOP之前的對象)和代理對象(應用AOP之後的對象)是一樣的
也就是
應用系統的其他部分不用為了支持代理對象而改變
織入 (Weaving)
織入是將切面應用到目標對象從而創建一個新的代理對象的過程
切面在指定接入點被織入到目標對象中
二
幾個重要接口
用於找到用於通知的相關的類型和方法
View Code
public interface IPointcut
{
ITypeFilter TypeFilter { get; }
IMethodMatcher MethodMatcher { get; }
}
用於匹配相關類型
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)
方法用來測試這個切入點是否匹配目標類的指定方法
這將在AOP代理被創建的時候執行
這樣可以避免在每次方法調用的時候都執行
如果兩個參數的matches方法對於一個給定的方法返回true
並且IMethodMatcher接口的IsRuntime方法也返回true
那麼有三個參數的matches方法將在每個方法調用時被調用
這使得切入點在通知將被執行前可以查看傳入到方法的參數
絕大多數的IMethodMatcher接口是static的
這也就意味著它們的IsRuntime屬性返回false
在這種情況下
有三個參數的Matches方法將永遠不會被調用
三
通知
通知生命周期
通知類型
一
攔截環繞通知(around advice)
Spring
NET中最基本的通知類型是攔截環繞通知(interception around advice)
即方法攔截器
攔截環繞通知繼承IMethodInterceptor接口
注意其中IMethodInvocation
Proceed()方法的調用
該方法會依次調用攔截器鏈上的其它攔截器
大部分攔截器都需要調用這個方法並返回它的返回值
當然
也可以不調用Proceed方法
而返回一個其它值或拋出一個異常
但一般不太會這麼做
二
前置通知(before advise)
是在IMethodInterceptor
Proceed()方法調用前的通知
繼承自IMethodBeforeAdvice接口
三
異常通知(throws advise)
是在IMethodInterceptor
Proceed()方法調用時發生異常的通知
繼承自IthrowsAdvice接口
IthrowsAdvice接口沒有定義任何方法
它是一個標識接口(按
之所以用標識接口
原因有二
在通知方法中
只有最後一個參數是必須的
如果聲明為接口的方法
參數列表就被固定了
如果第一個原因可以用重載的接口方法解決
那麼這個原因就是使用標識接口的充分原因了
實現此接口的類必須聲明一或多個通知方法
接口方法做不到這一點)
用以表明實現它的類聲明了一或多個強類型的異常通知方法
四
後置通知(after returning advise)
是在IMethodInterceptor
Proceed()方法調用後的通知
繼承自IAfterReturningAdvice接口
後置通知對切入點的執行沒有影響
如果通知拋出異常
就會沿攔截器鏈向上拋出
從而中斷攔截器鏈的繼續執行
Interception Around Advice(環繞攔截通知
後面的講解以環繞攔截通知為例子)
方法攔截器接口
View Code
public interface IMethodInterceptor : IInterceptor
{
object Invoke(IMethodInvocation invocation)
}
模擬環繞攔截通知
View Code
public class DebugInterceptor : IMethodInterceptor
{
public object Invoke(IMethodInvocation invocation)
{
Console
WriteLine(
Before: invocation=[{
}]
invocation)
object rval = invocation
Proceed()
Console
WriteLine(
Invocation returned
)
return rval;
}
}
注意 IMethodInvocation 的 Proceed 方法
proceed方法返回方法的返回值
四
切入點操作
靜態切入點
ProxyFactoryObject 顯式創建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>
不過由於顯式的創建AOP在要創建多個代理的時候需要重復的配置因此Spring提供了自動代理
ObjectNameAutoProxyCreator 對象名稱自動切入點
可以用特定的文本值或通配符匹配目標對象的名稱
並為滿足條件的目標對象創建AOP代理
該類支持模式匹配字符串
如
*name
name*
*name*
和精確文本如
name
我們可以通過下面這個簡單的例子了解一下自動代理的功能
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> 測試方法
[TestMethod]
public void ObjectNameAutoProxyCreatorMethodTest()
{
var context = ContextRegistry
GetContext()
IDictionary validates = context
GetObjectsOfType(typeof(IValidateService))
foreach (DictionaryEntry validate in validates)
{
((IValidateService) validate
Value)
Validate(null)
}
}
執行結果
ObjectNameAutoProxyCreatorMethodTest : Passed
Method:Validate開始執行
Method:Validate執行完畢
SdkRegularExpressionMethodPointcut 通過正則表達式來匹配需要執行的類或方法
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 類中的以Advance開頭的方法
DefaultAdvisorAutoProxyCreator+RegularExpressionMethodPointcutAdvisor 創建正則表達式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>
特性Aop
可以通過Attribute類來實現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>
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28013.html