面向切面程式設計:Autofac.Annotation擴充元件是我開源的一款利用打标簽完成autofac容器的注入元件。
https://github.com/yuzd/Autofac.Annotation
我們之前介紹了利用Aspect标簽來完成攔截器功能
Aspect是一對一的方式,我想要某個class開啟攔截器功能我需要針對每個class去配置。 詳情請點選
比如說 我有2個 controller 每個controller都有2個action方法,
[Component]
public class ProductController
{
public virtual string GetProduct(string productId)
{
return "GetProduct:" + productId;
}
public virtual string UpdateProduct(string productId)
{
return "UpdateProduct:" + productId;
}
}
[Component]
public class UserController
{
public virtual string GetUser(string userId)
{
return "GetUser:" + userId;
}
public virtual string DeleteUser(string userId)
{
return "DeleteUser:" + userId;
}
}
如果我需要這2個controller的action方法都在執行方法前打log 在方法執行後打log 按照上一節Aspect的話 我需要每個controller都要配置。如果我有100個controller的畫我就需要配置100次,這樣我覺得太麻煩了。是以我參考了Spring的Pointcut切面程式設計的方式實作了一套類似的,下面看如何用Pointcut的方式友善的配置一種切面去适用于N個對象。
定義一個切面:建立一個class 上面打上Pointcut的标簽 如下:
Pointcut标簽類有如下屬性:
屬性名 | 說明 |
---|---|
Name | 名稱Pointcut切面的名稱(預設為空,和攔截方法進行比對,參考下面說明) |
RetType | 比對目标類的方法的傳回類型(預設是%) |
NameSpace | 比對目标類的namespace(預設是%) |
ClassName | 比對目标類的類名稱(必填) |
MethodName | 比對目标類的方法名稱(預設是%) |
比對算法
舉例:
比對結果 | 比對模闆 | 要比對的字元串 |
---|---|---|
比對結果:true | "%" | "" |
" " | ||
"asdfa asdf asdf" | ||
比對結果:false | "_" | |
"4" | ||
"C" | ||
"CX" | ||
"[ABCD]" | ||
"A" | ||
"b" | ||
"X" | ||
"AB" | ||
"[B-D]" | ||
"D" | ||
"[^B-D]" | ||
"%TEST[ABCD]XXX" | "lolTESTBXXX" | |
"lolTESTZXXX" | ||
"%TEST[^ABCD]XXX" | ||
"%TEST[B-D]XXX" | ||
"%TEST[^B-D]XXX" | ||
"%Stuff.txt" | "Stuff.txt" | |
"MagicStuff.txt" | ||
"MagicStuff.txt.img" | ||
"Stuff.txt.img" | ||
"MagicStuff001.txt.img" | ||
"Stuff.txt%" | ||
"%Stuff.txt%" | ||
"%Stuff%.txt" | ||
"MagicStuff001.txt" | ||
"Stuff%.txt%" | ||
"%Stuff%.txt%" | ||
"?Stuff?.txt?" | "1Stuff3.txt4" | |
"1Stuff.txt4" | ||
"1Stuff3.txt" | ||
"Stuff3.txt4" |
// *Controller 代表比對 隻要是Controller結尾的類都能比對
// Get* 代表上面比對成功的類下 是以是Get打頭的方法都能比對
[Pointcut(ClassName = "*Controller",MethodName = "Get*")]
public class LoggerPointCut
{
}
定義好了一個Pointcut切面後 需要定義這個切面的攔截方法
配合Pointcut切面标簽,可以在打了這個标簽的class下定義攔截方法, 在方法上得打上特定的标簽,有如下幾種:
切面執行方法上打标簽種類 | |
---|---|
Before标簽 | 在比對成功的類的方法執行前執行 |
After标簽 | 在比對成功的類的方法執行後執行 |
Around标簽 | 承接了 比對成功的類的方法的執行權(如果一個切面配置了Around又配置了Before或者After,那麼會隻執行Around) |
以上3種标簽有一個可選的參數:Name (預設為空,可以和Pointcut的Name進行mapping)
- 因為一個class上可以打多個Pointcut切面,一個Pointcut切面可以根據name去比對對應攔截方法
// *Controller 代表比對 隻要是Controller結尾的類都能比對
// Get* 代表上面比對成功的類下 是以是Get打頭的方法都能比對
[Pointcut(ClassName = "*Controller",MethodName = "Get*")]
public class LoggerPointCut
{
/// <summary>
/// 打上Before标簽 代表滿足比對的方法 在執行之前會執行下面的Before()方法
/// </summary>
[Before]
public void Befor()
{
Console.WriteLine("before");
}
/// <summary>
/// 打上After标簽 代表滿足比對的方法 在執行之前會執行下面的After()方法
/// </summary>
[After]
public void After()
{
Console.WriteLine("after");
}
}
如果是用Around環繞的話
// *Controller 代表比對 隻要是Controller結尾的類都能比對
// Get* 代表上面比對成功的類下 是以是Get打頭的方法都能比對
[Pointcut(ClassName = "*Controller",MethodName = "Get*")]
public class LoggerPointCut
{
/// <summary>
/// 打上Around标簽 承接了 比對成功的類的方法的執行權
/// </summary>
/// <param name="context"></param>
[Around]
public void Around(AspectContext context)
{
//執行原目标方法前
Console.WriteLine(context.InvocationContext.MethodInvocationTarget.Name + "-->Start");
//執行原目标方法
context.InvocationProceedInfo.Invoke();
//執行原目标方法後
Console.WriteLine(context.InvocationContext.MethodInvocationTarget.Name + "-->End");
}
}
執行方法的參數說明:
執行方法的參數可以是DI容器的類型,會在執行時自動注入進來,可以使用Autowired,Value等标簽來修飾參數。
還有一個特殊的執行參數可以注入,那就是AspectContext,這個類型裡面你可以擷取到被攔截的方法的資訊,以及執行原方法的委托。
注意:這個執行後 有2種
- 正常執行成功
- 有異常,若方法參數注入了AspectContext 那麼可以通過Exception屬性值獲得異常内容
按照上面的配置
- ProductController.GetProduct 會被比對
- UserController.GetUser 會被比對
在執行上面這2個方法的時候會
- 先執行 LoggerPointCut.Before方法
- 再執行 LoggerPointCut.After方法
利用Autofac的這個開源擴充元件很友善的實作切面程式設計,總結切面程式設計三步驟
- 1.定義一個切面,編寫要比對的目标類的方法(采用sql的like方式比對),是以一個切面可以攔截n個目标
- 2.定義對應的攔截方法
- 3.執行被比對的方法時會執行對應的攔截方法
利用切面程式設計來自動加事物的背景系統例子
https://github.com/yuzd/AntMgr/wiki
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”将是我最大的寫作動力!歡迎各位轉載,轉載文章之後須在文章頁面明顯位置給出作者和原文連接配接,謝謝。