AOP原理及其實現


   AOP Aspect-Oriented programming 的縮寫,中文翻譯為面向切面編程,它是OOPObject-Oriented Programing,面向對象編程)的補充和完善。它和OOP一樣是一種編程思想。

AOP基本概念

橫切(cross-cutting):與對象核心功能無關的公共行為

關注點(concern):一塊我們感興趣的區域

方面(Aspect):就是將那些與業務無關,卻為業務模塊所共同調用的邏輯或責任封裝起來,便於減少系統的重復代碼,降低模塊間的耦合度,並有利於未來的可操作性和可維護性。

連接點(join point):是程序執行中一個精確執行點,例如類中的一個方法。它是一個抽象的概念,在實現AOP時,並不需要去定義一個連接點。

切入點(point cut):本質是一個捕獲連接點的結構。在AOP中,可以定義一個切入點來捕獲相關方法的調用。

通知(advice):是“切入點”的執行方代碼,是執行“方面”的具體邏輯

引入(introduce):為對象引入附加的方法或屬性,從而達到修改對象結構的目的

AOP本質
    OOP通過封裝、繼承及多態等概念來建立一種對象層次結構,用來模式現實事物。因為OOP中每個對象都是獨立的,因此當我們需要引入公共行為時(日志、安全、異常),代碼顯得比較臃腫。而AOP很好的解決了這一問題。它剖開對象內部把”橫切”的代碼封裝成一個“方面”。引用大牛的形象比喻來說“如果說“對象”是一個空心的圓柱體,其中封裝的是對象的屬性和行為;那么面向方面編程的方法,就仿佛一把利刃,將這些空心圓柱體剖開,以獲得其內部的消息。而剖開的切面,也就是所謂的“方面”了。然后它又以巧奪天功的妙手將這些剖開的切面復原,不留痕跡。”
    AOP把軟件系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程部分是核心關注點,與之關系不大的部分是橫切關注點。AOP的作用在於分離系統中各種關注點,將核心關注點和橫切關注點分離開來。

AOP實現方式
    實現AOP的方式有兩種:一是動態代理技術,二是采用靜態織入方式
    動態代理:利用截取消息的方式,對該消息進行裝飾,以取代原有對象行為的執行。一般通過Emit來動態的創建代理類;
    靜態織入:通過引入特定的語法來創建“方面”,從而在編譯期間就織入了“方面”的代碼。目前這種實現方式,都是對編譯器擴展。這種實現方式性能是最好的

采用動態代理實現AOP
    在說動態代理之前,我們要看一下之前是怎么實現“橫切”的。
    首先,我們有這樣一個類,負責處理用戶的業務邏輯。

   public interface IUserService
        {
            bool AddUser(User user);
            IList<User> GetUsers();
        }


        public class UserService : IUserService
        {
            public bool AddUser(User user)
            {
                Console.WriteLine("添加人員:{0}", user.Name);
                return true;
            }

            public IList<User> GetUsers()
            {
                return new List<User>
                {
                    new User{Name = "張三",BirthDate = DateTime.Now.AddYears(-1)},
                    new User{Name = "李四",BirthDate = DateTime.Now.AddYears(-2)},
                    new User{Name = "王五",BirthDate = DateTime.Now.AddYears(-3)},
                };
            }
        }

    現在我們要給所有的核心業務添加日志記錄的功能,我們以下幾種選擇:
1、直接在方法內部添加日志記錄的代碼

   public class UserService : IUserService
        {
            public bool AddUser(User user)
            {
                Console.WriteLine("添加人員:{0}", user.Name);
                Console.WriteLine("Log:AddUser");

                return true;
            }

            public IList<User> GetUsers()
            {
                Console.WriteLine("Log:GetUsers");
                return new List<User>
                {
                    new User{Name = "張三",BirthDate = DateTime.Now.AddYears(-1)},
                    new User{Name = "李四",BirthDate = DateTime.Now.AddYears(-2)},
                    new User{Name = "王五",BirthDate = DateTime.Now.AddYears(-3)},
                };
            }
        }

2、添加一個繼承UserServeice的子類,並重寫/覆蓋父類的方法

  public class MyUserService : UserService
        {
            public new bool AddUser(User user)
            {
                base.AddUser(user);
                Console.WriteLine("Log:AddUser");
                return true;
            }

            public new IList<User> GetUsers()
            {
                Console.WriteLine("Log:GetUsers");
                return base.GetUsers();
            }

        }

3、寫一個靜態的代理類
首先聲明一個攔截器接口

  public interface IInterceptor
        {
            object Invoke(object obj, string methodName, object[] paramters);
        }

然后創建一個日志攔截器

 public class LogInterceptor : IInterceptor
        {
            public object Invoke(object obj, string methodName, object[] paramters)
            {
                Console.WriteLine("Log:{0}", methodName);

                object result = obj.GetType().GetMethod(methodName).Invoke(obj, paramters);

                return result;
            }
        }

最后創建靜態代理類

        public class MyUserServiceProxy
        {
            private readonly IInterceptor _interceptor;
            private readonly User _user;
            public MyUserServiceProxy(User user, IInterceptor interceptor)
            {
                _user = user;
                _interceptor = interceptor;
            }

            public bool AddUser(User user)
            {
                return (bool)_interceptor.Invoke(_user, "AddUser", new object[] { user });
            }

            public IList<User> GetUsers()
            {
                return (IList<User>)_interceptor.Invoke(_user, "GetUsers", null);

            }


        }

下面調用

var proxy = new AOPTest.MyUserServiceProxy(new AOPTest.User
            {
                Name = "Khadron",
                BirthDate = DateTime.Now
            },
                new AOPTest.LogInterceptor());

            IList<AOPTest.User> users = proxy.GetUsers();

            if (users.Count > 0)
            {
                proxy.AddUser(users[0]);
            }


            Console.ReadKey();

    相對於前面的兩種方法,第三種方法算是比較好的了,不過還是不夠好,缺點是不僅必須給每個業務類都重寫一個包含日志處理的代理類,而且如果還有其他類似日志處理的模塊(權限),那還需要在寫一個其他代理類,這樣做起來不僅工作量大,而且維護起來也十分麻煩。 采用動態代理的方式很好的解決了這個問題,上面說過主要采用Emit來實現,代碼太多就不貼了,請查看我的github,這里面實現了一個簡單的AOP框架

.NET平台下AOP框架
    動態代理:Castle Dynamic ProxyUnityLOOM.NET
    靜態織入:Postsharp(收費)Eos

個人推薦使用 Castle框架,至此結束


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM