当前位置: 代码迷 >> 综合 >> 编程思想: 面向切面编程(Aspect-Oreinted Programming - AOP)
  详细解决方案

编程思想: 面向切面编程(Aspect-Oreinted Programming - AOP)

热度:61   发布时间:2023-12-15 10:58:50.0

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

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

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

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

Python实现

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

import timedef 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 freturn 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 {
    @Overridepublic 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());}@Overridepublic void remind() {
    authorize();  // 授权reminder.remind();  // 调用代理类的业务逻辑log();  // 打日志}
}

Client可以这样使用代理类:

// Client.java
public class Client {
    @Testpublic 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*/@Overridepublic 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 {
    @Overridepublic void before() {
    System.out.println("Authorization: OK.");}}private static class ReminderAfterAdvice implements AfterAdvice {
    @Overridepublic void after() {
    System.out.println("Log info: Remind at " + new Date().getTime());}}@Testpublic void clientOfProxyFactory() {
    Reminder reminder = (Reminder)new ProxyFactory(new ReminderBeforeAdvice(),new ReminderImpl(),new ReminderAfterAdvice()).createProxy();reminder.remind();}
}
  相关解决方案