AOP 到底是什么?一篇文章彻底搞懂:AOP = 动态代理 + 方法拦截 + 增强逻辑
很多 Java 开发者在工作中天天使用 AOP。
比如:
@Transactional
public void saveUser(){
}
或者:
@Log
public void query(){
}
甚至:
@Before
public void checkPermission(){
}
但是很多人只知道:
Spring AOP 很强,可以实现事务、日志、权限控制。
真正问:
Spring AOP 底层为什么可以做到?
很多人就开始模糊。
其实 AOP 的核心并没有那么神秘。
一句话总结:
AOP = 动态代理 + 方法拦截 + 增强逻辑
下面我们拆开来看。
一、为什么需要 AOP?
先看一个业务。
假设我们有一个订单服务:
public class OrderService {
public void createOrder(){
System.out.println("创建订单");
}
}
业务很简单。
但是随着项目发展,我们需要增加:
1. 日志
每次调用记录:
谁调用了
什么时候调用
执行多久
例如:
public void createOrder(){
System.out.println("开始执行");
System.out.println("创建订单");
System.out.println("执行结束");
}
2. 权限校验
例如:
创建订单之前:
if(!user.hasPermission()){
throw new RuntimeException();
}
3. 事务
例如:
订单创建失败:
需要回滚。
问题来了:
这些代码是不是业务代码?
不是。
创建订单:
属于业务。
日志:
属于系统功能。
权限:
属于系统功能。
事务:
属于系统功能。
如果全部写进去:
public void createOrder(){
checkPermission();
beginTransaction();
log();
//真正业务
createOrder();
commit();
}
最后业务类变成:
业务代码
+
日志代码
+
权限代码
+
事务代码
非常混乱。
所以提出:
AOP
把这些公共功能抽出去。
最终:
业务代码:
public void createOrder(){
System.out.println("创建订单");
}
但是执行的时候:
自动增加:
权限
↓
日志
↓
事务
↓
业务方法
↓
事务提交
↓
日志记录
这就是 AOP。
二、AOP 的核心思想:代理
那么问题来了:
你没有修改:
OrderService
为什么执行的时候多出了东西?
答案:
代理对象
假设原对象:
OrderService
Spring 创建一个代理:
OrderServiceProxy
以后调用:
orderService.createOrder();
实际上:
调用的是:
OrderServiceProxy.createOrder()
代理里面:
public void createOrder(){
checkPermission();
log();
target.createOrder();
saveLog();
}
所以:
AOP 本质就是:
用代理对象包裹真实对象,在调用真实方法之前和之后执行额外逻辑。
三、什么是动态代理?
代理有两种:
1. 静态代理
自己写代理类。
例如:
目标:
public class UserService{
public void save(){
System.out.println("保存用户");
}
}
代理:
public class UserServiceProxy{
private UserService target;
public void save(){
System.out.println("日志开始");
target.save();
System.out.println("日志结束");
}
}
问题:
如果有100个类:
你写100个代理。
太麻烦。
2. 动态代理
动态代理:
不用自己写代理类。
程序运行的时候自动生成。
例如:
UserService
|
|
↓
动态生成
|
|
↓
UserServiceProxy
Java提供:
JDK动态代理
要求:
必须实现接口。
例如:
public interface UserService{
void save();
}
CGLIB动态代理
不需要接口。
直接继承。
例如:
原类:
public class UserService{
public void save(){
}
}
CGLIB生成:
public class UserServiceProxy
extends UserService{
}
Spring现在默认更倾向使用 CGLIB。
四、方法拦截:AOP真正执行的位置
代理生成了。
但是:
怎么知道什么时候执行增强逻辑?
答案:
方法拦截
例如 CGLIB:
public Object intercept(
Object obj,
Method method,
Object[] args,
MethodProxy proxy
)
所有方法调用都会进入:
intercept()
例如:
调用:
userService.save();
实际上:
Proxy.save()
|
|
↓
intercept()
|
|
↓
执行增强逻辑
|
|
↓
调用真实save()
五、增强逻辑是什么?
增强逻辑就是:
你想额外增加的代码。
比如:
Before
方法执行前:
@Before
public void before(){
System.out.println("检查权限");
}
流程:
before
↓
目标方法
After
方法执行后:
@After
public void after(){
System.out.println("记录日志");
}
流程:
目标方法
↓
after
Around
环绕通知。
最强。
例如:
@Around
public Object around(
ProceedingJoinPoint point
)
throws Throwable{
System.out.println("开始");
Object result =
point.invoke();
System.out.println("结束");
return result;
}
执行:
开始
↓
目标方法
↓
结束
甚至:
point.invoke();
不执行。
那么:
目标方法不会执行。
六、把三个东西组合起来
现在:
AOP =
① 动态代理
负责:
创建代理对象。
例如:
UserService
↓
UserServiceProxy
② 方法拦截
负责:
拦截方法调用。
例如:
save()
↓
intercept()
③ 增强逻辑
负责:
添加额外功能。
例如:
before
after
around
组合:
调用方法
↓
代理对象
↓
方法拦截
↓
执行增强逻辑
↓
目标方法
这就是完整 AOP。
七、手写一个简单 AOP
我们模拟 Spring。
目标类:
public class UserService {
public void save(){
System.out.println("保存用户");
}
}
定义增强:
public class LogAspect {
public void before(){
System.out.println("方法开始");
}
public void after(){
System.out.println("方法结束");
}
}
创建代理:
Enhancer enhancer =
new Enhancer();
enhancer.setSuperclass(
UserService.class
);
enhancer.setCallback(
new MethodInterceptor(){
public Object intercept(
Object obj,
Method method,
Object[] args,
MethodProxy proxy
)
throws Throwable{
System.out.println("before");
Object result =
proxy.invokeSuper(
obj,
args
);
System.out.println("after");
return result;
}
}
);
UserService proxy =
(UserService)
enhancer.create();
调用:
proxy.save();
输出:
before
保存用户
after
是不是和 Spring AOP 一模一样?
只是 Spring 帮你自动完成:
- 找切面
- 找切点
- 创建代理
- 织入增强
八、Spring AOP 为什么使用 BeanPostProcessor?
因为 Spring 创建 Bean 有生命周期:
创建对象
↓
初始化
↓
放入IOC容器
其中:
BeanPostProcessor 可以插入:
初始化前
初始化后
例如:
public Object postProcessAfterInitialization(
Object bean
){
return proxy;
}
原来:
UserService对象
替换:
UserService代理对象
所以:
@Autowired 注入的其实不是原对象。
而是:
代理对象。
九、多个 AOP 怎么办?
例如:
一个方法:
@Auth
@Transactional
@Log
public void save(){
}
三个增强:
权限
日志
事务
怎么办?
答案:
AOP链
类似:
责任链模式。
结构:
Proxy
|
|
权限Interceptor
|
|
日志Interceptor
|
|
事务Interceptor
|
|
target
执行:
权限before
日志before
事务开启
业务方法
事务提交
日志after
权限after
这也是 Spring AOP 底层思想。
十、面试总结
如果面试官问:
"你理解 AOP 吗?"
可以回答:
AOP 的核心思想是通过代理对象对目标对象进行增强,本质是动态代理、方法拦截以及增强逻辑的组合。Spring AOP 在 Bean 生命周期中通过 BeanPostProcessor 判断目标对象是否匹配切点,如果匹配则创建代理对象。调用代理方法时进入拦截器链,在目标方法执行前后织入 Before、After、Around 等增强逻辑。多个切面通过责任链模式组成 interceptor chain 顺序执行。
最后一句话记忆
不要背 Spring AOP。
记住这个模型:
业务对象
↓
动态代理包装
↓
拦截方法调用
↓
执行增强逻辑
↓
调用真实方法
所以:
AOP不是魔法,它就是一个会自动生成的代理对象,在调用方法的时候偷偷插入一些代码。
理解这一句话,Spring AOP 的大门就打开了。