C#進階系列——AOP?AOP!

前言:今天大閱兵,但是苦逼的博主還得坐在電腦前寫博客,爲了弄清楚AOP,博主也是拼了。這篇打算寫寫AOP,提及AOP,其實博主接觸這個概念也才幾個月,瞭解後才知道,原來以前本身寫的好多代碼原理就是基於AOP的,好比MVC的過濾器Filter,它裏面的異常捕捉能夠經過FilterAttribute,IExceptionFilter去處理,這兩個對象的處理機制內部原理應該就是AOP,只不過以前沒有這個概念罷了。編程

1、AOP概念

老規矩,仍是先看官方解釋:AOP(Aspect-Oriented Programming,面向切面的編程),它是能夠經過預編譯方式和運行期動態代理實如今不修改源代碼的狀況下給程序動態統一添加功能的一種技術。它是一種新的方法論,它是對傳統OOP編程的一種補充。OOP是關注將需求功能劃分爲不一樣的而且相對獨立,封裝良好的類,並讓它們有着屬於本身的行爲,依靠繼承和多態等來定義彼此的關係;AOP是但願可以將通用需求功能從不相關的類當中分離出來,可以使得不少類共享一個行爲,一旦發生變化,沒必要修改不少類,而只須要修改這個行爲便可。AOP是使用切面(aspect)將橫切關注點模塊化,OOP是使用類將狀態和行爲模塊化。在OOP的世界中,程序都是經過類和接口組織的,使用它們實現程序的核心業務邏輯是十分合適。可是對於實現橫切關注點(跨越應用程序多個模塊的功能需求)則十分吃力,好比日誌記錄,權限驗證,異常攔截等。架構

博主的理解:AOP就是將公用功能提取出來,若是之後公用功能的需求發生變化,只須要改動公用的模塊的代碼便可,多個調用的地方則不須要改動。所謂面向切面,就是隻關注通用功能,而不關注業務邏輯。實現方式通常是經過攔截。好比,咱們隨便一個Web項目基本都有的權限驗證功能,進入每一個頁面前都會校驗當前登陸用戶是否有權限查看該界面,咱們不可能說在每一個頁面的初始化方法裏面都去寫這段驗證的代碼,這個時候咱們的AOP就派上用場了,AOP的機制是預先定義一組特性,使它具備攔截方法的功能,可讓你在執行方法以前和以後作你想作的業務,而咱們使用的時候只須要的對應的方法或者類定義上面加上某一個特性就行了。框架

2、使用AOP的優點

博主以爲它的優點主要表如今:ide

一、將通用功能從業務邏輯中抽離出來,能夠省略大量重複代碼,有利於代碼的操做和維護。模塊化

二、在軟件設計時,抽出通用功能(切面),有利於軟件設計的模塊化,下降軟件架構的複雜度。也就是說通用的功能都是一個單獨的模塊,在項目的主業務裏面是看不到這些通用功能的設計代碼的。測試

3、AOP的簡單應用

爲了說明AOP的工做原理,博主打算先從一個簡單的例子開始,經過靜態攔截的方式來了解AOP是如何工做的。this

一、靜態攔截

    public class Order
    {
        public int Id { set; get; }
        public string Name { set; get; }
        public int Count { set; get; }
        public double Price { set; get; }
        public string Desc { set; get; }
    }

    public interface IOrderProcessor
    {
        void Submit(Order order);
    }
    public class OrderProcessor : IOrderProcessor
    {
        public void Submit(Order order)
        {
            Console.WriteLine("提交訂單");
        }
    }

    public class OrderProcessorDecorator : IOrderProcessor
    {
        public IOrderProcessor OrderProcessor { get; set; }
        public OrderProcessorDecorator(IOrderProcessor orderprocessor)
        {
            OrderProcessor = orderprocessor;
        }
        public void Submit(Order order)
        {
            PreProceed(order);
            OrderProcessor.Submit(order);
            PostProceed(order);
        }
        public void PreProceed(Order order)
        {
            Console.WriteLine("提交訂單前,進行訂單數據校驗....");
            if (order.Price < 0)
            {
                Console.WriteLine("訂單總價有誤,請從新覈對訂單。");
            }
        }

        public void PostProceed(Order order)
        {
            Console.WriteLine("提交帶單後,進行訂單日誌記錄......");
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "提交訂單,訂單名稱:" + order.Name + ",訂單價格:" + order.Price);
        }
    }

調用代碼:spa

     static void Main(string[] args)
        {
            Order order = new Order() { Id = 1, Name = "lee", Count = 10, Price = 100.00, Desc = "訂單測試" };
            IOrderProcessor orderprocessor = new OrderProcessorDecorator(new OrderProcessor());
            orderprocessor.Submit(order);
            Console.ReadLine();
        }

獲得結果:設計

上面咱們模擬訂單提交的例子,在提交一個訂單前,咱們須要作不少的準備工做,好比數據有效性校驗等;訂單提交完成以後,咱們還須要作日誌記錄等。上面的代碼很簡單,沒有任何複雜的邏輯,從上面的代碼能夠看出,咱們經過靜態植入的方式手動在執行方法前和執行方法後讓它作一些咱們須要的功能。AOP的實現原理應該也是如此,只不過它幫助咱們作了方法攔截,幫咱們省去了大量重複代碼,咱們要作的僅僅是寫好攔截前和攔截後須要處理的邏輯。3d

二、動態代理

瞭解了靜態攔截的例子,你是否對AOP有一個初步的認識了呢。下面咱們就來到底AOP該如何使用。按照園子裏面不少牛人的說法,AOP的實現方式大體能夠分爲兩類:動態代理和IL 編織兩種方式。博主也不打算照本宣科,分別拿Demo來講話吧。下面就以兩種方式各選一個表明框架來講明。

動態代理方式,博主就以微軟企業庫(MS Enterprise Library)裏面的PIAB(Policy Injection Application Block)框架來做說明。

首先須要下載如下幾個dll,而後添加它們的引用。

而後定義對應的Handler

   public class User
    {
        public string Name { set; get; }
        public string PassWord { set; get; }
    }

    #region 一、定義特性方便使用
    public class LogHandlerAttribute : HandlerAttribute
    {
        public string LogInfo { set; get; }
        public int Order { get; set; }
        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return new LogHandler() { Order = this.Order, LogInfo = this.LogInfo };
        }
    }
    #endregion

    #region 二、註冊對須要的Handler攔截請求
    public class LogHandler : ICallHandler
    {
        public int Order { get; set; }
        public string LogInfo { set; get; }

        //這個方法就是攔截的方法,能夠規定在執行方法以前和以後的攔截
        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            Console.WriteLine("LogInfo內容" + LogInfo);
            //0.解析參數
            var arrInputs = input.Inputs;
            if (arrInputs.Count > 0)
            {
                var oUserTest1 = arrInputs[0] as User;
            }
            //1.執行方法以前的攔截
            Console.WriteLine("方法執行前攔截到了");
            //2.執行方法
            var messagereturn = getNext()(input, getNext);

            //3.執行方法以後的攔截
            Console.WriteLine("方法執行後攔截到了");
            return messagereturn;
        }
    }
    #endregion

    #region 三、用戶定義接口和實現
    public interface IUserOperation
    {
        void Test(User oUser);
        void Test2(User oUser, User oUser2);
    }


    //這裏必需要繼承這個類MarshalByRefObject,不然報錯
    public class UserOperation : MarshalByRefObject, IUserOperation
    {
        private static UserOperation oUserOpertion = null;
        public UserOperation()
        {
            //oUserOpertion = PolicyInjection.Create<UserOperation>();
        }

        //定義單例模式將PolicyInjection.Create<UserOperation>()產生的這個對象傳出去,這樣就避免了在調用處寫這些東西
        public static UserOperation GetInstance()
        {
            if (oUserOpertion == null)
                oUserOpertion = PolicyInjection.Create<UserOperation>();

            return oUserOpertion;
        }
        //調用屬性也會攔截
        public string Name { set; get; }

        //[LogHandler],在方法上面加這個特性,只對此方法攔截
        [LogHandler(LogInfo = "Test的日誌爲aaaaa")]
        public void Test(User oUser)
        {
            Console.WriteLine("Test方法執行了");
        }

        [LogHandler(LogInfo = "Test2的日誌爲bbbbb")]
        public void Test2(User oUser, User oUser2)
        {
            Console.WriteLine("Test2方法執行了");
        }
    }
    #endregion

最後咱們來看調用的代碼:

        static void Main(string[] args)
        {
            try
            {
                var oUserTest1 = new User() { Name = "test2222", PassWord = "yxj" };
                var oUserTest2 = new User() { Name = "test3333", PassWord = "yxj" };
                var oUser = UserOperation.GetInstance();
                oUser.Test(oUserTest1);
                oUser.Test2(oUserTest1,oUserTest2);
            }
            catch (Exception ex)
            {
                //throw;
            }
        }

獲得結果以下:

咱們來看執行Test()方法和Test2()方法時候的順序。

因爲Test()和Test2()方法上面加了LogHander特性,這個特性裏面定義了AOP的Handler,在執行Test和Test2方法以前和以後都會進入Invoke()方法裏面。其實這就是AOP的意義所在,將切面的通用功能在統一的地方處理,在主要邏輯裏面直接用過特性使用便可。

三、IL編織

靜態織入的方式博主打算使用PostSharp來講明,一來這個使用起來簡單,二來項目中用過這種方式。

Postsharp從2.0版本就開始收費了。爲了說明AOP的功能,博主下載了一個免費版本的安裝包,使用PostSharp與其它框架不太同樣的是必定要下載安裝包安裝,只引用類庫是不行的,由於上文說過,AOP框架須要爲編譯器或運行時添加擴展。使用步驟以下:

(1)下載Postsharp安裝包,安裝。

(2)在須要使用AOP的項目中添加PostSharp.dll 這個dll的引用。

(3)定義攔截的方法:

    [Serializable]
    public class TestAop : PostSharp.Aspects.OnMethodBoundaryAspect
    {
     //發生異常時進入此方法
public override void OnException(MethodExecutionArgs args) { base.OnException(args); }
     //執行方法前執行此方法
public override void OnEntry(MethodExecutionArgs args) { base.OnEntry(args); }
     //執行方法後執行此方法
public override void OnExit(MethodExecutionArgs args) { base.OnExit(args); } }

注意這裏的TestAop這個類必需要是可序列化的,因此要加上[Serializable]特性

(4)在須要攔截功能的地方使用。

在類上面加特性攔截,此類下面的全部的方法都會具備攔截功能。

  [TestAop]public class Impc_TM_PLANT : Ifc_TM_PLANT
    {
        /// <summary>
        /// 獲取或設置服務接口。
        /// </summary>
        private Ic_TM_PLANTService service { get; set; }
        
        public IList<DTO_TM_PLANT> Find()
        {
            DTO_TM_PLANT otest = null;
            otest.NAME_C = "test";//異常,會進入OnException方法
        return service.FindAll(); 
     }
  }

方法上面加特性攔截,只會攔截此方法。

        [TestAop]
        public IList<DTO_TM_PLANT> Find()
        {
            DTO_TM_PLANT otest = null;
            otest.NAME_C = "test";
            return service.FindAll();
        }

有沒有感受很簡單,很強大,其實這一簡單應用,解決咱們常見的日誌、異常、權限驗證等功能簡直過小菜一碟了。固然Postsharp可能還有許多更加高級的功能,有興趣能夠深究下。

四、MVC裏面的Filter

   public class AOPFilterAttribute : ActionFilterAttribute, IExceptionFilter
    {

        public void OnException(ExceptionContext filterContext)
        {
            throw new System.NotImplementedException();
        }
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            
            base.OnActionExecuting(filterContext);
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            base.OnActionExecuted(filterContext);
        }
    }

在controller裏面使用該特性:

     [AOPFilter]
        public JsonResult GetEditModel(string strType)
        {
            var lstRes = new List<List<DragElementProp>>();
            var lstResPage = new List<PageProperty>();

       //.........todo
return Json(new { lstDataAttr = lstRes, PageAttr = lstResPage, lstJsConnections = lstJsPlumbLines }, JsonRequestBehavior.AllowGet); }

調試可知,在執行GetEditModel(string strType)方法以前,會先執行OnActionExecuting()方法,GetEditModel(string strType)以後,又會執行OnActionExecuted()方法。這在咱們MVC裏面權限驗證、錯誤頁導向、日誌記錄等經常使用功能均可以方便解決。

 

附上源碼。有興趣能夠下載看看。

相關文章
相關標籤/搜索