开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情
AOP:我们常说
AOP可以对已有功能进行增强。那么为什么需要用AOP对功能进行增强呢?
场景
A工程是一个旧工程,它之前对数据库的操作没有通过日志进行保存,现在需要你进行业务升级。升级的内容很简单:记录每个数据库操作的动作。倘若有n个业务操作,我们就需要对n个操作内容进行升级。
这里有一个关键点:
- 重复的操作
| daoA | daoB |
|---|---|
| Log() | Log() |
| SELECT... | UPDATE... |
注意看 Log() 在每个持久层时都是需要重新升级的内容,这就是需要增强的地方,以横着的角度看这些不同类中的相同的操作,这种增强方式就是AOP。
那么也就是说,针对这种复杂重复的功能增强,Spring给我们提供了AOP。
在学习Spring提供的AOP方法之前,我们先从静态代理以及动态代理学习。
静态代理
静态代理是依靠:聚合的方式实现的,它在编译之前就用代码定义好了。我们通过简单的代码可以模拟一下:
- 这里有一个衣服厂家类,它有一个方法
getClothes,作用是打印一下生产衣服
public class FactoryClothes implements FactoryInterface{
public void getClothes(){
System.out.println("生产衣服");
}
}
- 这里我们静态定义一个代理类,通过聚合的方式传入
Target真正执行生产衣服的厂商类,通过接口控制声明和厂商同样的方法getClothes,但其真正执行的是厂商类,而代理类可以做方法前后的增强处理。
public class FactoryAgency implements FactoryInterface{
FactoryClothes factoryClothes;
public FactoryAgency(FactoryClothes factoryClothes) {
this.factoryClothes = factoryClothes;
}
public void getClothes(){
System.out.println("代理人执行");
factoryClothes.getClothes();
System.out.println("代理人执行结束");
}
}
动态代理
动态代理的一个特点是,代理是在程序运行时创建的。实现动态代理我们需要用到InvocationHandler
这我们演示一个例子,看看动态代理是如何实现方法增强的。
- 我们需要一个被代理类,与上面相同。
public class FactoryClothes implements FactoryInterface{
public void getClothes(){
System.out.println("生产衣服");
}
}
- 代理类是我们运行时才生成的,也就是当代理类执行目标方法时,用
InvocationHandler,对执行方法进行拦截。我们需要用到InvocationHandler定义一个类,这个类还是和静态代理一样接收Target类,但是InvocationHandler中的invoke方法是拦截Target执行方法,拦截后可以通过编写其他非功能性代码对Target的方法进行增强
public class ProxyFactory implements InvocationHandler {
Object obj;
// 传入被代理对象
public ProxyFactory(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理人执行");
// 拦截其执行方法,通过反射的方式让Target类执行方法
Object object = method.invoke(Object, args);
System.out.println("代理人执行结束");
return object;
}
}
- 使用
Proxy.newProxyInstance生成代理类 在Main方法中,核心语句是这个
FactoryInterface proxy = (FactoryInterface) Proxy.newProxyInstance(FactoryClothes.class.getClassLoader(), FactoryClothes.class.getInterfaces(), new ProxyFactory(factoryClothes));
- 参数1:用哪个类加载器去加载代理对象,一般是接口的类加载器
- 参数2:动态代理类需要实现的接口
- 参数3:动态代理方法在执行时,会调用实现
InvocationHandler的invoke方法去执行