SSM 框架从入门到入土:AOP 面向切面编程(全链路讲解)
AOP(Aspect Oriented Programming)是 Spring 框架的第二大核心(第一是 IoC),也是 SSM 项目中最常用、最能体现框架价值的部分之一。
很多人学 AOP 学到“晕”,其实核心就一句话:
把散落在业务代码各处的公共逻辑(日志、事务、权限、异常处理、性能监控、接口限流……)抽取出来,写成独立的模块,然后在合适的位置自动织入,业务代码只管写核心逻辑。
下面从零基础 → 能写 → 能优化 → 能讲原理的完整路径带你吃透 AOP。
1. 先搞清楚 AOP 到底在解决什么问题(业务痛点)
没有 AOP 的原始写法(你一定会这样写):
@Service
public class OrderService {
public void createOrder(Order order) {
// 1. 记录日志
log.info("开始创建订单,用户ID:{}", order.getUserId());
// 2. 权限校验
if (!permissionService.hasPermission("order:create")) {
throw new PermissionDeniedException();
}
try {
// 3. 开启事务
transaction.begin();
// 核心业务逻辑
orderDao.insert(order);
userDao.updateBalance(order.getUserId(), order.getAmount());
// 4. 提交事务
transaction.commit();
// 5. 记录成功日志
log.info("订单创建成功,订单号:{}", order.getOrderNo());
} catch (Exception e) {
// 6. 回滚事务
transaction.rollback();
// 7. 记录异常日志
log.error("订单创建失败", e);
throw e;
}
// 8. 性能监控
long end = System.currentTimeMillis();
monitor.record("createOrder", end - start);
}
}
问题:
- 业务方法里塞满了与核心逻辑无关的代码
- 重复代码到处都是,维护噩梦
- 想统一改日志格式?要改几十上百个地方
用了 AOP 之后(理想状态):
@Service
public class OrderService {
@Transactional
@Loggable("创建订单")
@Permission("order:create")
@Monitor("createOrder")
public void createOrder(Order order) {
// 只剩核心业务逻辑
orderDao.insert(order);
userDao.updateBalance(order.getUserId(), order.getAmount());
}
}
AOP 帮你把日志、事务、权限、监控等横切关注点(Cross-Cutting Concerns)剥离出去。
2. AOP 核心概念(背下来就能讲)
| 概念 | 中文 | 解释 | 对应代码例子 |
|---|---|---|---|
| Aspect | 切面 | 封装横切逻辑的模块(类) | 日志切面、事务切面 |
| Join Point | 连接点 | 程序执行过程中可以插入切面的点(Spring AOP 只支持方法) | 方法调用、方法执行、异常抛出 |
| Pointcut | 切点 | 决定在哪些连接点上执行通知的规则(表达式) | execution(* com.xxx.service..(..)) |
| Advice | 通知/增强 | 具体要执行的代码,以及执行时机 | @Before、@After、@Around 等 |
| Weaving | 织入 | 把切面应用到目标对象的过程(编译期/类加载期/运行期) | Spring AOP 是运行期动态代理 |
| Target | 目标对象 | 被增强的原始对象 | OrderService |
| Proxy | 代理对象 | 容器最终给用户使用的对象(包含增强逻辑) | 动态生成的代理类 |
3. Spring AOP 支持的 5 种通知类型(最重要)
| 通知类型 | 注解 | 执行时机 | 是否能控制目标方法执行 | 是否能修改返回值 | 是否能捕获异常 |
|---|---|---|---|---|---|
| 前置通知 | @Before | 目标方法之前 | 否 | 否 | 否 |
| 后置通知 | @After | 目标方法之后(无论成功失败) | 否 | 否 | 否 |
| 返回通知 | @AfterReturning | 目标方法正常返回后 | 否 | 可以获取返回值 | 否 |
| 异常通知 | @AfterThrowing | 目标方法抛出异常后 | 否 | 否 | 可以捕获异常 |
| 环绕通知 | @Around | 包围目标方法(最强大) | 能 | 能 | 能 |
@Around 是万能的,其他四种通知最终都可以用 @Around 实现。
4. 切点表达式(Pointcut Expression)最常用写法(背 8 条就够)
// 最常用:匹配任意包下任意类的任意方法
execution(* *..*.*(..))
// 指定包
execution(* com.xxx.service.*.*(..)) // service 包下所有类所有方法
execution(* com.xxx.service..*.*(..)) // service 包及其子包
// 指定返回类型
execution(public * com.xxx..*.*(..)) // 返回 public 方法
execution(* com.xxx..*.add*(..)) // 方法名以 add 开头
// 参数匹配
execution(* add(..)) // 任意参数
execution(* add(String, ..)) // 第一个参数 String,其余任意
execution(* add(String, int)) // 精确匹配两个参数
// 常用组合
@annotation(com.xxx.annotation.Loggable) // 带有某个注解的方法
within(com.xxx.service.*) // 类型匹配
bean(orderService) // bean 名称匹配
小技巧:@annotation 是项目中最常用的方式(自定义注解 + AOP)
5. 快速上手:三种常用实现方式对比
| 方式 | 配置量 | 主流程度 | 推荐场景 |
|---|---|---|---|
| XML 配置 | 多 | 低 | 老项目、必须统一管理 |
| @AspectJ 注解 | 少 | ★★★★★ | 绝大多数现代项目 |
| 纯 Java 配置(@Bean) | 中 | 中 | 零配置、模块化强 |
最推荐:@AspectJ 注解方式(Spring 官方也主推)
@Aspect
@Component
public class LogAspect {
// 定义切点(可复用)
@Pointcut("@annotation(com.xxx.annotation.Loggable)")
public void logPointcut() {}
@Around("logPointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
// 获取方法签名、参数等
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getName();
Object[] args = joinPoint.getArgs();
log.info("开始执行方法:{},参数:{}", methodName, Arrays.toString(args));
try {
// 执行目标方法
Object result = joinPoint.proceed();
long cost = System.currentTimeMillis() - start;
log.info("方法 {} 执行完成,耗时 {}ms,返回:{}", methodName, cost, result);
return result;
} catch (Throwable e) {
log.error("方法 {} 执行异常", methodName, e);
throw e;
}
}
}
6. AOP 底层原理(面试必问)
Spring AOP 基于动态代理实现,主要两种:
- JDK 动态代理(默认,如果目标类实现了接口)
- 必须实现接口
- 生成的代理类实现同一接口
- CGLIB 动态代理(如果没有接口,或强制使用)
- 通过继承目标类实现
- final 类、final 方法无法代理
Spring 4.x 之后默认策略:
- 如果目标类实现了接口 → 用 JDK 动态代理
- 否则 → 用 CGLIB
Spring Boot 2.x+ 默认全用 CGLIB(更通用)
核心流程:
- 容器启动 → 解析 @Aspect 类
- 根据 Pointcut 找到所有匹配的 Join Point
- 创建代理对象(Proxy)
- 调用时 → 代理对象先执行 Advice → 再调用目标方法(或不调用)
7. SSM 项目中最常见的 AOP 应用场景(直接抄)
- 统一日志(操作日志、接口日志、慢查询日志)
- 统一异常处理(@ControllerAdvice + AOP)
- 事务管理(@Transactional 就是 AOP 实现的)
- 权限控制(自定义 @Permission + AOP)
- 接口限流(令牌桶、滑动窗口)
- 方法耗时监控 + 告警
- 数据脱敏(返回前对手机号、身份证脱敏)
- 重复提交防止(幂等性)
- 缓存切面(@Cacheable/@CacheEvict 也是 AOP)
8. 从入门到入土的进阶 checklist
- [ ] 会写 @Aspect + 5 种通知
- [ ] 能手写常用切点表达式
- [ ] 能用 @Around 实现任意增强
- [ ] 知道 @Order / Ordered 控制切面执行顺序
- [ ] 能解释 JoinPoint / ProceedingJoinPoint 区别
- [ ] 明白 JDK vs CGLIB 区别和选择依据
- [ ] 能手写一个统一日志 + 异常处理的切面
- [ ] 知道 AOP 性能开销(环绕通知最重)
- [ ] 能说出 AOP 不能代理的情况(内部调用、静态方法、final 方法)
如果你能把上面 checklist 全打勾,AOP 基本就“入土”了。
想直接上手哪一块?
比如:
- 统一日志切面完整代码
- 自定义注解 + AOP 实现权限控制
- AOP + 事务传播行为结合
- AOP 失效的 10 种常见场景
告诉我,我直接给你代码 + 原理 + 踩坑总结!