什么是AOP
AOP: Aspect-Oriented Programming 面向切面编程。
AOP编程是相对于OOP编程的,在OOP编程中,我们经常会像下面这么写:
因为OOP方法是面向对象编程的,所以我们经常调用一个对象的方法,然后这个方法中又会调用另外一个对象的run方法。
在AOP编程中,我们将OOP编程里的方法进入之前和结束时候的点称之为切面,也就是说咱们想在进入一个方法的前后都预先做一些事情,
比如我们想打LOL,那我们就要先打开电脑,然后才可以打开LOL,最后打完了我们再关闭电脑。
如下图,就是我们在执行一个方法(动作)前,进行一些拦截动作,称之为before(之前),after(之后)切面。
AOP适合用于哪些场景
下面列举几个需要使用AOP的场景,但是它们的核心都是方法拦截:
- 日志: 在访问一个方法的时候,将方法的调用参数和返回结果都打印出来。
- 缓存: 某个方法只被调用一次后,将结果返回并保存起来,之后不再调用这个方法,直接返回之前保存的结果。
- 鉴权: 在调用一个方法前,判断是否有权限调用它。
首先我们先用OOP的模式实现一下方法拦截的场景,然后再用AOP的方式来实现一下。
使用OOP的话,我们就需要用到装饰器模式
。
装饰器模式
什么是装饰器模式?
- 装饰器模式:Decorator pattern
- 动态的为一个对象增加功能,但是不改变其结构
- 本质上是一个"包装"
打个比方,我们现在有一个方法是撸铁健身,但是在这之前我们可能需要先吃饭等等,才有力气去健身,这时候我们有两种选择,
一是修改这个方法的代码,在这个方法刚开始的地方添加吃饭的代码
改造前
public class DataService {
public void 撸铁健身() {
System.out.println("开始健身");
}
}
改造后
public class DataService {
public void 撸铁健身() {
System.out.println("开始吃饭");
System.out.println("开始健身");
}
}
二是使用一个包装类,包装一下当前方法的类
首先写出一个撸铁健身方法的接口类和实现类:
public interface DataService {
String 撸铁健身(String name);
}
public class DataServiceImpl implements DataService {
public String 撸铁健身(String name) {
System.out.println(name + "正在健身中");
return "健身完毕";
}
}
static DataService service = new DataServiceImpl();
public static void main(String [] args) {
service.撸铁健身("张三");
}
随后我们添加一个装饰器类,开始我们的装饰器模式
public class EatDataServiceImpl implements DataService {
// 委托类,真正的撸铁健身类
DataService delegate;
public EatDataServiceImpl(DataService dataService) {
this.delegate = dataService;
}
public String 撸铁健身(String name) {
System.out.println(name + "正在吃饭中");
return delegate.撸铁健身(name);
}
}
static DataService service = new EatDataServiceImpl(new DataServiceImpl());
public static void main(String [] args) {
service.撸铁健身("张三");
}
这时候输出的结果就是
张三正在吃饭中
张三正在健身中
装饰器模式的好处就是可以不改动原本类的任何代码,在外面套一层就可以增加额外的功能,
当然这种实现也依赖于接口这个特性,我们定义好一个没有实现的接口,真正实现的时候,不用关心到底是 DataServiceImpl
还是 EatDataServiceImpl
,
只要真正的实现类实现了我们的接口,就可以正常使用。
利用装饰器模式,我们就可以实现在调用真正的核心类DataServiceImpl.撸铁健身
方法前做一些日志、鉴权以及缓存的功能了。
下面我们实现一个缓存的装饰类:
public class CacheDataServiceImpl implements DataService {
// 缓存
private Map<String,String> cache;
// 委托类,真正的撸铁健身类
DataService delegate;
public CacheDataServiceImpl(DataService dataService) {
this.delegate = dataService;
this.cache = new HashMap<>();
}
public String 撸铁健身(String name) {
// 如果张三已经健身过就直接从缓存中返回健身完毕
String cacheResult = cache.get(name);
if (cacheResult == null) {
cacheResult = delegate.撸铁健身(name);
cache.put(name,cacheResult);
}
return cacheResult;
}
}
static DataService service = new CacheDataServiceImpl(new EatDataServiceImpl(new DataServiceImpl()))
public static void main(String [] args) {
System.out.println(service.撸铁健身("张三"));
System.out.println(service.撸铁健身("张三"));
System.out.println(service.撸铁健身("张三"));
}
这时候第一次输出的结果就是
张三正在吃饭中
张三正在健身中
健身完毕
因为张三已经健身完了,所以结果被缓存起来了,其余调用都是
健身完毕
源代码在:https://github.com/qiaomengnan16/DecoratorPatternDemo
下图是MyBatis的一个类,可以看见,MyBatis也使用了装饰器模式,在 CachingExecutor 类中有一个 delegate,
用于执行真正业务方法,
CachingExecutor 这个类作用于一些缓存操作,因为数据库的IO操作开销比较大,所以某些情况下一些结果被MyBatis放在了缓存中,
当缓存中没有或者需要刷新缓存的时候,调用委托类 delegate 的方法。
最后总结一下就是,当多个不同的实现类实现同一个接口的时候,我们可以使用装饰器模式为他们增加额外的功能。
AOP的实现在下一篇写,下面是链接地址:https://juejin.im/post/6865916387471310856