AI智能
改变未来

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

面向对象编程(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@timerdef test():time.sleep(1)if __name__ == \'__main__\':test()

Java实现

要实现一个

Reminder

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

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

Reminder

的实现如下

// ReminderImpl.javapublic class ReminderImpl implements Reminder {@Overridepublic void remind() {System.out.println(\"Reminder: Wake up!\");}}

1. 静态代理

创建一个代理类

ReminderProxy

, 只负责实现授权和打日志功能. 业务逻辑的处理则调用

Reminder

中的方法.

// ReminderProxy.javapublic 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.javapublic 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的思想). 我们考虑一个非常简单的模型, 即在代理对象执行的方法之前和之后分别加入

bofore

after

的方法.

  • 定义新功能的接口:
    BeforeAdvice

    : 在代理方法之前执行(例如, 授权)

  • AfterAdvice

    : 在代理方法之后执行(例如, 打日志)

// BeforeAdvice.javapublic interface BeforeAdvice {public void before();}// AfterAdvice.javapublic 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通过实现
    BeforeAdvice

    AfterAdvice

    为代理对象添加新的功能.

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();}}
  • 点赞
  • 收藏
  • 分享
  • 文章举报

胡拉哥发布了33 篇原创文章 · 获赞 2 · 访问量 1549私信关注

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » 编程思想: 面向切面编程(Aspect-Oreinted Programming – AOP)