天天看點

Autofac的切面程式設計實作

面向切面程式設計: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 比對目标類的方法名稱(預設是%)

比對算法

Autofac的切面程式設計實作

舉例:

比對結果 比對模闆 要比對的字元串
比對結果: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

如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”将是我最大的寫作動力!歡迎各位轉載,轉載文章之後須在文章頁面明顯位置給出作者和原文連接配接,謝謝。

繼續閱讀