1. 什么是AOP
在谈到spring的时候,我们总是会听到这2个名词:
- IOC:全称叫Inversion of Control,翻译过来叫控制反转
- AOP:全称叫Aspect Oriented Programming,翻译过来叫面向切面编程
那么什么是面向切面编程呢?这是一种软件设计思想,一种设计模式,并不指具体的代码,一个良好的AOP设计,可以让我们更好的遵循软件设计的2个重要原则:
- SRP原则:Single Responsibility Principle,单一职责原则
- DRY原则:Don’t Repeat Yourself,不要重复原则
关于IOC可以参考我的另一篇文章谈谈对Spring的理解之IOC
2. SRP原则所解决的问题
在我们非常熟悉的面对对象过程中,我们会把一个一个的实体,抽象成一个类,同时把一段业务代码逻辑封装成一个方法。比如,我们现在有一个方法pay,很好的封装了付款的逻辑
// 付款方法
public void pay() {
// 很好的封装了付款的代码逻辑
}
随着系统业务的复杂化,现在要求在付款前,加上一个权限验证的方法。那么这个时候代码就变成了这样
// 付款方法
public void pay() {
// 加一段权限验证的代码逻辑
// 很好的封装了付款的代码逻辑
}
这个时候,就已经违反了代码的单一职责原则,即一个类一个方法,应该只做一件事情,那么pay方法,就应该只是高内聚付款的代码逻辑即可,而不应该加上权限验证的代码逻辑(注意,这只是站在设计的角度,做讲解用,实际运用中不一定)。那么违反这个原则会带来什么不好的影响呢? 这个例子中是看不出有什么影响的。但是注意,在实际的系统中,可能会有非常多的地方调用pay这个方法,比如突然有一天,来自功能1的地方调用pay方法不需要加权限验证了,而功能2、3、4、5的地方依然还要加上权限验证,这个时候,就体现出违反SRP所带来的后果了。代码变成了这样:
// 付款方法
public void pay(boolean isSource1) {
if (!isSource1) {
// 不是来着功能1的调用,才需要加上权限验证
// 加一段权限验证的代码逻辑
}
// 很好的封装了付款的代码逻辑
}
而因为这一个改动,所有调用pay方法的地方,都要去修改,如果系统有上百个地方要调用了?甚至系统中有的功能写的程序员离职了,你还得去看他之前的功能,来判断这一次修改影不影响之前的功能。所以这就是不遵循SRP带来的问题。
3. DRY原则所解决的问题
对于前面的问题,会有人质疑,那权限验证方法不要加在pay方法里,直接在调用的地方加不就好了。这确实保证了pay方法的SRP,但是又违反了DRY的原则。比如系统中有上百个地方调用pay,那么是不是要在每一个调用的地方加上权限验证?而且,这个权限验证的代码是硬编码在代码里,不可以通过配置的方式,来决定这个方法加不加权限验证。
在举个例子,有3个方法,test1、test2、test3,都有自己的业务逻辑:
public void test1() {
// test1 的业务
}
public void test2() {
// test2 的业务
}
public void test3() {
// test3 的业务
}
这个时候,有一个需求,要在test1、test2、test3方法结束后,加一段日志记录的功能,并且是可以灵活配置的。那么在每个方法结束后,都加上一个日志记录的代码
public void test1() {
// test1 的业务
// 日志记录代码第一行
// 日志记录代码第二行
// ....
}
public void test2() {
// test2 的业务
// 日志记录代码第一行
// 日志记录代码第二行
// ....
}
public void test3() {
// test3 的业务
// 日志记录代码第一行
// 日志记录代码第二行
// ....
}
这里就违反了DRY原则,即代码重复了,如果日志记录的代码改动,那3个调用的地方都要改,会出现漏改或者错改的情况。
其实这里大家都知道,正确的做法是讲日志记录抽象出一个方法log,然后test1、2、3都调用这个方法就好了,这样日志记录的代码改动,只要改log里面的逻辑就好:
public void test1() {
// test1 的业务
// 调用日志记录
log();
}
public void test2() {
// test2 的业务
// 调用日志记录
log();
}
public void test3() {
// test3 的业务
// 调用日志记录
log();
}
// 通用日志记录
public void log() {
// 日志记录代码第一行
// 日志记录代码第二行
// ....
}
确实,这很好的遵循了DRY原则。可是AOP赋予了我们更好的特性,即可以通过配置的方式来动态决定方法的调用
4. AOP的实现原理
spring的AOP是通过动态代理的方式实现的,关于动态代理,可以参考我的另一篇文章动态代理设计模式
spring中我们常见的AOP应用有:
- 事务处理
- 日志记录
- 权限验证
- 异常处理
- 性能监控
- …