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

2019-09-14  本文已影响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对动态代理提供了一下支持:

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

// BeforeAdvice.java
public interface BeforeAdvice {
    public void before();
}

// AfterAdvice.java
public interface AfterAdvice {
    public void after();
}
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
        );
    }
}
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();
    }
}
上一篇下一篇

猜你喜欢

热点阅读