听到这个名字的时候,我真的一头雾水。其实面向切面编程(AOP)是相对面向对象编程(OOP)来说的。Emmm,还是不懂。下面就用一个事例来说明面向切面编程吧。
需求
我们现在有 DataService 一个返回数据的服务,我们想每次执行里面方法的时候都打日志。
装饰器
我们很容易想到就是用装饰器模式去实现,比如
class DataService {
public void getData() {}
}
class Logger implements DataService {
// 委托
private DataService delegate;
public Logger(DataService delegate) {
this.delegate = delegate;
}
@Override
public void getData() {
打 Log
delegate.getData();
}
}
// 多态
DataService service = new Logger(new DataServiceImpl());
service.getData();
一层包一层,调用 getData()
的时候,其实是调用了 Logger.getData()
。如果我们再加更多的功能,可能会变成这样:
DataService service = new Auth(new Cache(new Logger(new DataService())));
md,我都忘了要写多少个右括号了。。。这个方法是其实没什么问题,但是也有它的缺点:
- 代码有冗余。这里并不是说包得太多层,而是每次方法你都要先
打Log
再调用方法,如果 DataService 有10个方法,那么打Log
就要写10次。 - 这里我们注意到 Logger 是要继承 DataService 的,因此 Logger 会从 DataService 那继承一些不必要的东西。
AOP
那还有什么高招呢?这里提供一个思路:我们搞一个类 X,当调用 x.getData()
的时候,X 先打Log
,再调用 dataService.getData()
。
???,这不废话,听起来和上面一样啊,注意这里其实是做了代理。比如,我在中国上 github.com,proxy 将请求转到日本服务器,再转到美国服务器。上 github.com 就是例子中的 getData()
。
而装饰器模式有点像自己抱着电脑飞到美国上 github.com。
说了那么多,先看代码。
这里先定义好代理。
public class LogProxy implements InvocationHandler {
DataService delegate;
public LogProxy(DataService delegate) {
this.delegate = delegate;
}
@Override // method -> 原来的方法
public Object invoke(Object proxy, Method, method, Object[] args) {
System.out.println("Log");
// 调用原来的方法
Object returnValue = method.invoke(delegate, args)
System.out.println("Finish");
return returnValue;
}
}
开始“网上冲浪”。
DataService service = new DataServiceImpl();
// ds 已不再是原来的 DataService,是动态生成的实例
DataService ds = Proxy.newProxyInstance(service.getClass().getClassLoader(),
new Class[]{DataService.class}, // 要拦截哪些接口
new LogProxy(service))
ds.getData(); // 会先调用 invoke 方法,再去调用 getData() 方法
但是这里的 Proxy 只能用于拦截接口的方法,不能使用类。如果要代理类的话,需要用到 ByteBuddy。
最后
AOP 其实有点像我们所说的中间件,为什么叫“切面”呢?其实这和中间件的意思差不多嘛。装饰器也可以搞定,但是缺点有很多,因此最好用 AOP。看到这么复杂的代码不禁会心一笑,这些使用了 AOP 模式的插件一定都帮我弄好了,一般不用自己实现 :)
网友评论