zoukankan      html  css  js  c++  java
  • 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框架,至此结束

  • 相关阅读:
    冒泡排序
    数据结构和算法关系
    js获取ifram对象
    java STL
    bufferedReader 乱码问题
    css animation让图标不断旋转
    apply通过实例理解
    jquery.ajaxfileupload.js
    JDBC getMetaData将结果集组装到List
    Android开发之使用BaseAdapter的notifyDataSetChanged()无法更新列表
  • 原文地址:https://www.cnblogs.com/Khadron/p/AOP.html
Copyright © 2011-2022 走看看