程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#進階系列——AOP?AOP!

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

編輯:C#入門知識

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


前言:今天大閱兵,可是苦逼的博主還得坐在電腦前寫博客,為了弄清楚AOP,博主也是拼了。這篇打算寫寫AOP,說起AOP,其實博主接觸這個概念也才幾個月,了解後才知道,原來之前自己寫的好多代碼原理就是基於AOP的,比如MVC的過濾器Filter,它裡面的異常捕捉可以通過FilterAttribute,IExceptionFilter去處理,這兩個對象的處理機制內部原理應該就是AOP,只不過之前沒有這個概念罷了。   一、AOP概念 老規矩,還是先看官方解釋:AOP(Aspect-Oriented Programming,面向切面的編程),它是可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一添加功能的一種技術。它是一種新的方法論,它是對傳統OOP編程的一種補充。OOP是關注將需求功能劃分為不同的並且相對獨立,封裝良好的類,並讓它們有著屬於自己的行為,依靠繼承和多態等來定義彼此的關系;AOP是希望能夠將通用需求功能從不相關的類當中分離出來,能夠使得很多類共享一個行為,一旦發生變化,不必修改很多類,而只需要修改這個行為即可。AOP是使用切面(aspect)將橫切關注點模塊化,OOP是使用類將狀態和行為模塊化。在OOP的世界中,程序都是通過類和接口組織的,使用它們實現程序的核心業務邏輯是十分合適。但是對於實現橫切關注點(跨越應用程序多個模塊的功能需求)則十分吃力,比如日志記錄,權限驗證,異常攔截等。   博主的理解:AOP就是將公用功能提取出來,如果以後公用功能的需求發生變化,只需要改動公用的模塊的代碼即可,多個調用的地方則不需要改動。所謂面向切面,就是只關注通用功能,而不關注業務邏輯。實現方式一般是通過攔截。比如,我們隨便一個Web項目基本都有的權限驗證功能,進入每個頁面前都會校驗當前登錄用戶是否有權限查看該界面,我們不可能說在每個頁面的初始化方法裡面都去寫這段驗證的代碼,這個時候我們的AOP就派上用場了,AOP的機制是預先定義一組特性,使它具有攔截方法的功能,可以讓你在執行方法之前和之後做你想做的業務,而我們使用的時候只需要的對應的方法或者類定義上面加上某一個特性就好了。   二、使用AOP的優勢 博主覺得它的優勢主要表現在:   1、將通用功能從業務邏輯中抽離出來,可以省略大量重復代碼,有利於代碼的操作和維護。   2、在軟件設計時,抽出通用功能(切面),有利於軟件設計的模塊化,降低軟件架構的復雜度。也就是說通用的功能都是一個單獨的模塊,在項目的主業務裡面是看不到這些通用功能的設計代碼的。   三、AOP的簡單應用 為了說明AOP的工作原理,博主打算先從一個簡單的例子開始,通過靜態攔截的方式來了解AOP是如何工作的。   1、靜態攔截  
    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);
        }
    }

 

    調用代碼:    
     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的實現原理應該也是如此,只不過它幫助我們做了方法攔截,幫我們省去了大量重復代碼,我們要做的僅僅是寫好攔截前和攔截後需要處理的邏輯。   2、動態代理 了解了靜態攔截的例子,你是否對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 1、定義特性方便使用
    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 2、注冊對需要的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 3、用戶定義接口和實現
    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的意義所在,將切面的通用功能在統一的地方處理,在主要邏輯裡面直接用過特性使用即可。   3、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可能還有許多更加高級的功能,有興趣可以深究下。   4、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裡面權限驗證、錯誤頁導向、日志記錄等常用功能都可以方便解決。    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved