一文彻底搞懂 Spring AOP(概念、原理、使用、执行流程、面试重点)
在 Spring 开发中,我们经常看到很多注解:
@Transactional
@Cacheable
@Async
@Log
这些注解看起来只是简单标记了一下方法,但实际上它们可以:
- 自动开启事务
- 自动记录日志
- 自动缓存数据
- 自动异步执行
很多人会问:
Spring 是怎么做到的?
答案就是:
AOP(面向切面编程)
本篇文章将系统讲清 Spring AOP 的核心知识体系。
一、什么是 AOP
1 AOP 概念
AOP 全称:
Aspect Oriented Programming
面向切面编程
核心思想:
在不修改原有代码的情况下
对程序功能进行增强
简单理解:
在方法执行前后插入代码
例如:
java
public void createUser(){
System.out.println("创建用户");
}
我们希望:
执行前记录日志
执行后记录日志
变成:
java
log.info("方法开始");
createUser();
log.info("方法结束");
如果直接写在代码中:
业务代码会变得混乱
AOP 的目的就是:
把这些公共逻辑提取出来
统一处理
二、为什么需要 AOP
在真实项目中,有很多 重复逻辑:
例如:
日志
事务
权限校验
缓存
监控
如果每个方法都写:
java
public void createUser(){
log.info("开始");
transaction.begin();
//业务代码
transaction.commit();
}
会出现问题:
大量重复代码
难以维护
AOP 可以把这些逻辑统一管理:
日志模块
事务模块
权限模块
业务代码只需要关注:
核心业务
三、AOP 核心术语
学习 AOP 必须理解 5 个概念。
1 切面(Aspect)
切面就是:
增强逻辑 + 切入点
例如:
日志切面
事务切面
权限切面
示例:
java
@Aspect
@Component
public class LogAspect {
}
2 连接点(JoinPoint)
连接点指的是:
可以被拦截的方法
例如:
Controller方法
Service方法
简单理解:
程序执行的某个点
3 切入点(Pointcut)
切入点指的是:
具体拦截哪些方法
例如:
拦截所有Service方法
示例:
java
@Pointcut("execution(* com.demo.service.*.*(..))")
public void pointcut(){}
4 通知(Advice)
通知就是:
增强逻辑
例如:
日志
权限
事务
通知类型:
| 类型 | 说明 |
|---|---|
| @Before | 方法执行前 |
| @After | 方法执行后 |
| @AfterReturning | 返回后 |
| @AfterThrowing | 异常后 |
| @Around | 环绕 |
5 目标对象(Target)
目标对象就是:
原始业务对象
例如:
java
UserService
四、Spring AOP 执行流程
假设调用:
userService.createUser()
执行流程:
Controller
↓
代理对象
↓
Before通知
↓
目标方法
↓
After通知
完整流程:
调用代理对象
↓
执行前置通知
↓
执行目标方法
↓
执行后置通知
五、Spring AOP 的实现原理
Spring AOP 底层使用:
动态代理
Spring 会创建:
代理对象
替代原始对象。
调用流程:
Controller
↓
Proxy(UserService)
↓
UserService
代理对象负责:
增强逻辑
六、Spring AOP 两种代理方式
Spring AOP 有两种实现方式。
1 JDK 动态代理
条件:
目标类必须有接口
示例:
java
public interface UserService{
void createUser();
}
Spring 生成代理:
UserServiceProxy
代理接口。
特点:
基于接口
2 CGLIB 代理
如果没有接口:
Spring使用:
CGLIB
实现方式:
继承目标类
例如:
UserService$$EnhancerBySpring
七、Spring AOP 使用步骤
1 引入依赖
Spring Boot 已包含:
spring-boot-starter-aop
2 开启 AOP
java
@EnableAspectJAutoProxy
Spring Boot 默认已开启。
3 定义切面
java
@Aspect
@Component
public class LogAspect {
}
4 定义切入点
java
@Pointcut("execution(* com.demo.service.*.*(..))")
public void pointcut(){}
含义:
拦截service包所有方法
5 编写通知
前置通知
java
@Before("pointcut()")
public void before(){
System.out.println("方法执行前");
}
后置通知
java
@After("pointcut()")
public void after(){
System.out.println("方法执行后");
}
返回通知
java
@AfterReturning("pointcut()")
public void afterReturn(){
}
异常通知
java
@AfterThrowing("pointcut()")
public void exception(){
}
环绕通知(最强)
java
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("方法开始");
Object result = joinPoint.proceed();
System.out.println("方法结束");
return result;
}
八、切入点表达式(重点)
最常用表达式:
execution()
语法:
execution(访问修饰符 返回值 包名.类名.方法名(参数))
示例:
拦截 Service:
execution(* com.demo.service.*.*(..))
解释:
* 返回值任意
service包
任意类
任意方法
任意参数
九、Spring AOP 常见问题
1 同类方法调用失效
例如:
java
@Service
public class UserService {
public void methodA(){
methodB();
}
@Transactional
public void methodB(){
}
}
事务不会生效。
原因:
没有经过代理对象
2 private 方法无法增强
例如:
java
private void test(){}
原因:
CGLIB 无法重写 private 方法
3 final 方法无法增强
原因:
final 方法不能被重写
十、Spring AOP 应用场景
AOP 在实际项目中非常常见:
| 场景 | 示例 |
|---|---|
| 事务管理 | @Transactional |
| 日志记录 | 接口日志 |
| 权限控制 | 登录校验 |
| 性能监控 | 方法耗时 |
| 缓存 | @Cacheable |
十一、Spring AOP 与 AspectJ 区别
| Spring AOP | AspectJ | |
|---|---|---|
| 实现方式 | 动态代理 | 字节码增强 |
| 织入时间 | 运行时 | 编译时 |
| 功能 | 简单 | 强大 |
| 使用难度 | 简单 | 较复杂 |
Spring 默认:
Spring AOP
十二、Spring AOP 面试高频问题
1 什么是 AOP?
答:
AOP 是一种编程思想,
可以在不修改业务代码的情况下
对方法进行增强。
2 Spring AOP 原理?
动态代理
3 Spring AOP 有哪两种代理?
JDK 动态代理
CGLIB 代理
4 AOP 常见通知类型?
@Before
@After
@AfterReturning
@AfterThrowing
@Around
5 为什么事务会失效?
原因:
没有通过代理对象调用
例如:
同类方法调用
private方法
final方法
十三、Spring AOP 总结
Spring AOP 的核心思想:
在不修改业务代码的情况下
增强方法功能
核心原理:
动态代理
核心流程:
Controller
↓
代理对象
↓
增强逻辑
↓
目标方法
AOP 在 Spring 中应用非常广泛:
事务
日志
权限
缓存
监控
几乎所有 Spring 高级功能都基于 AOP 实现。