美文网首页
编程思想: 面向切面编程(Aspect-Oreinted Pro

编程思想: 面向切面编程(Aspect-Oreinted Pro

作者: 胡拉哥 | 来源:发表于2019-09-14 18:58 被阅读0次

    面向对象编程(Object-Orentied Programming - OOP)的特点是继承, 多态和封装, 其中封装指的是把属性和方法按类(Class)进行划分, 从而复用代码并降低编程的复杂性. 随着业务的变化, 项目中的类越来越多, 开发者又发现了新的问题:

    1. 不同类之间重复的代码越来越多. 例如每个类的方法中都要打日志.
    2. 方法中除了业务逻辑之外, 要实现大量与业务无关的功能, 例如调试, 缓存, 鉴权等.

    为了解决上述问题, 第一个思路是把重复的代码或者业务逻辑之外的功能抽象出来, 然后在新的类中实现. 这样一来新类与旧类在项目中就耦合了, 即, 新类的改变会影旧类. 第二个思路是在旧类需要的时候(编译或运行时)动态地加入这些功能, 即 面向切面编程. 切面代表了一个功能点, 它一般是对类的功能的补充. 站在业务的角度来看, 切面是与业务逻辑无关的功能.

    如果把面向对象编程看成是纵向的(类之间功能独立), 那么面向切面编程就是横向的, 它为纵向的类提供业务无关的能力, 因此面向切面编程可以看成是对面向对象编程的补充. 下面引入一些例子来介绍AOP的实现.

    Python实现

    Python是动态语言, 利用装饰器语法糖可以容易地实现切面. 下面是计时器的例子.

    import time
    
    
    def timer(func):
        """
        计时装饰器.
        :param func 被装饰的函数
        :return 装饰之后的函数(wrapper)
        """
        def wrapper(*args, **kwargs):
            """
            装饰之后的函数
            """
            start = time.time()
            f = func(*args, **kwargs)  # 执行被装饰的函数
            end = time.time()
            print("[%s] costs %.2f seconds" % (func.__name__, end-start))
            return f
        return wrapper
    
    
    @timer
    def test():
        time.sleep(1)
    
    
    if __name__ == '__main__':
        test()
    

    Java实现

    要实现一个Reminder接口, 其核心功能是发一条提醒的消息. 除此之外, 发消息之前需要取得授权, 成功发消息之后需要打一条日志. 在这里我们把授权和打日志看成是切面 (非业务逻辑). 为了实现切面功能, 我们需要用到设计模式中的 代理模式.

    // Reminder.java
    public interface Reminder {
        public void remind();
    }
    

    Reminder的实现如下

    // ReminderImpl.java
    public class ReminderImpl implements Reminder {
        @Override
        public void remind() {
            System.out.println("Reminder: Wake up!");
        }
    }
    

    1. 静态代理

    创建一个代理类ReminderProxy, 只负责实现授权和打日志功能. 业务逻辑的处理则调用Reminder中的方法.

    // ReminderProxy.java
    public class ReminderProxy implements Reminder {
    
        private Reminder reminder;  // 被代理的对象
    
        public ReminderProxy(Reminder reminder) {
            this.reminder = reminder;
        }
        
        private void authorize() {
            System.out.println("Authorization: OK.");
        }
        
        private void log() {
            System.out.println("Log info: Remind at " + new Date().getTime());
        }
    
        @Override
        public void remind() {
            authorize();  // 授权
            reminder.remind();  // 调用代理类的业务逻辑
            log();  // 打日志
        }
    }
    

    Client可以这样使用代理类:

    // Client.java
    public class Client {
        @Test
        public void clientOfProxy() {
            Reminder reminder = new ReminderProxy(new ReminderImpl());
            reminder.remind();
        }
    }
    

    静态代理虽然能为类增加新的功能, 但它依赖已经实现的类. 当有多个实现类时, 我们需要依次实现多个代理类, 这样并不能减少重复代码. 为了解决这个问题, 我们利用java的反射动态生成代理类, 即在编译或运行时生成类.

    2. 动态代理

    Java对动态代理提供了一下支持:

    • java.lang.reflect.Proxy 动态生成代理类和对象
    • java.lang.reflect.InvocationHandler 实现对代理类和对象的访问

    下面我们用动态代理的方法来实现上述功能(即AOP的思想). 我们考虑一个非常简单的模型, 即在代理对象执行的方法之前和之后分别加入boforeafter的方法.

    • 定义新功能的接口:
      • BeforeAdvice: 在代理方法之前执行(例如, 授权)
      • AfterAdvice: 在代理方法之后执行(例如, 打日志)
    // BeforeAdvice.java
    public interface BeforeAdvice {
        public void before();
    }
    
    // AfterAdvice.java
    public interface AfterAdvice {
        public void after();
    }
    
    • 创建代理工厂, 利用InvocationHandler调用被代理的对象.
    public class ProxyFactory {
    
        private BeforeAdvice beforeAdvice;  
        private Object object;  // 被代理的对象
        private AfterAdvice afterAdvice;
    
        public ProxyFactory(BeforeAdvice beforeAdvice, Object object, AfterAdvice afterAdvice) {
            this.beforeAdvice = beforeAdvice;
            this.object = object;
            this.afterAdvice = afterAdvice;
        }
        
        // 用来调用被代理对象
        private InvocationHandler invocationHandler = new InvocationHandler() {
            /**
             *
             * @param proxy 代理对象
             * @param method 被调用的方法
             * @param args  被调用方法的参数
             * @return 被调用方法的返回值
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 调用代理对象之前执行的方法
                if (beforeAdvice != null) beforeAdvice.before();
                // 调用被代理对象object的方法
                Object result = null;
                if (object != null) result = method.invoke(object, args);
                // 调用代理对象之后执行的方法
                if (afterAdvice != null) afterAdvice.after();
                return result;
            }
        };
        
        // 创建被代理对象
        public Object createProxy() {
            // Proxy.newProxyInstance参数:
            // 1. ClassLoader: 定义代理类的类加载器
            // 2. Class[]: 接口类的列表
            // 3. InvocationHandler: 调用方法的处理器
            return Proxy.newProxyInstance(
                    ClassLoader.getSystemClassLoader(),
                    object.getClass().getInterfaces(),
                    invocationHandler
            );
        }
    }
    
    • Client通过实现BeforeAdviceAfterAdvice为代理对象添加新的功能.
    public class Client {
    
        private static class ReminderBeforeAdvice implements BeforeAdvice {
            @Override
            public void before() {
                System.out.println("Authorization: OK.");
            }
        }
    
        private static class ReminderAfterAdvice implements AfterAdvice {
            @Override
            public void after() {
                System.out.println("Log info: Remind at " + new Date().getTime());
            }
        }
    
        @Test
        public void clientOfProxyFactory() {
            Reminder reminder = (Reminder)new ProxyFactory(
                    new ReminderBeforeAdvice(),
                    new ReminderImpl(),
                    new ReminderAfterAdvice()
            ).createProxy();
            reminder.remind();
        }
    }
    

    相关文章

      网友评论

          本文标题:编程思想: 面向切面编程(Aspect-Oreinted Pro

          本文链接:https://www.haomeiwen.com/subject/diohyctx.html