目录
[1. 痛点推导:我们为什么需要 AOP?](#1. 痛点推导:我们为什么需要 AOP?)
[2. AOP 与拦截器、全局异常处理的关系](#2. AOP 与拦截器、全局异常处理的关系)
[3. 方案落地:快速入门 AOP](#3. 方案落地:快速入门 AOP)
[4. 流程变化:加入 AOP 后发生了什么?](#4. 流程变化:加入 AOP 后发生了什么?)
1. 痛点推导:我们为什么需要 AOP?
假设我们现在有一个图书管理系统,里面有很多业务接口(如查询图书、新增图书等)。
某天,客户抱怨:"系统最近变慢了,接口耗时太长,需要优化!"
第一步: 我们得先找出到底哪个方法慢,这就需要统计所有业务方法的执行耗时。(这里没有是使用jmeter来进行,只是进行一个场景的模拟)
传统方案:
我们只能在每个业务方法的首尾加上时间记录代码:
java
public void function1() {
long startTime = System.currentTimeMillis(); // 记录开始时间
test1(); // 真正的业务逻辑
long endTime = System.currentTimeMillis(); // 记录结束时间
log.info("function1执行耗时: " + (endTime - startTime) + " ms");
}
方案推演出的死局:
一个项目中几十上百个接口,每个接口又有多个方法。如果每个方法都这么写:
-
代码严重冗余:时间统计的代码比业务代码还多。
-
侵入性极强:业务代码被非业务代码(日志打印)严重污染。
-
难以维护:哪一天不统计耗时了,或者统计逻辑变了,又要挨个方法去改。
思考: 有没有一种技术,能在不修改原始业务方法 的前提下,把"统计耗时"这个动作像包包子一样,自动包在原方法的外面?
这就是 AOP(Aspect Oriented Programming,面向切面编程) 诞生的意义。AOP 的本质,就是对某一类特定问题(如日志、事务、耗时统计)的集中处理,达到无侵入的解耦。
2. AOP 与拦截器、全局异常处理的关系
你可能用过拦截器(Interceptor)或 @ControllerAdvice。
-
拦截器 的维度是 URL(一次 HTTP 请求),它能处理登录校验,但它管不到普通类里的普通方法。
-
AOP 的维度更加细致 ,它可以精准定位到包、类、方法名、参数 级别。
简单来说:拦截器、全局异常处理,都是 AOP 思想在 Web 层面的一种具体实现。而 Spring AOP 则提供了更强大的底层支持。
-
简单来说:拦截器、统一异常处理、统一返回格式,都是 AOP 思想在 Web 层面的一种具体实现;而 Spring AOP 提供了更底层、更通用的支持。
技术 作用维度 典型场景 拦截器 URL 请求 登录校验、请求日志 @ControllerAdvice全局 Controller 层 全局异常处理、数据绑定 Spring AOP 包、类、方法、参数 方法耗时、事务、权限、日志
3. 方案落地:快速入门 AOP
需求: 统计 com.example.demo.controller 包下所有类、所有方法的执行时间。
第一步:引入依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
第二步:编写切面类
java
@Slf4j
@Aspect // 1. 告诉 Spring:这是一个切面类
@Component
public class TimeAspect {
// 2. @Around(环绕通知):表示在目标方法的前后都执行。括号里是切点表达式,定义了拦截哪些方法。
@Around("execution(* com.example.demo.controller.*.*(..))")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
// 【前置逻辑】
long begin = System.currentTimeMillis();
// 3. 调用 ProceedingJoinPoint.proceed() 执行原始方法
Object result = pjp.proceed();
// 【后置逻辑】
long end = System.currentTimeMillis();
log.info(pjp.getSignature() + "执行耗时: {}ms", end - begin);
return result; // 必须返回原始方法的执行结果
}
}
4. 流程变化:加入 AOP 后发生了什么?
在没有 AOP 之前,调用方(比如前端)请求 BookController 的 queryBookById 方法,是直接调用 目标对象。
加入 AOP 后,流程变成了:
-
调用方发起调用。
-
请求被 Spring AOP 拦截。
-
先执行我们写的前置逻辑(记录 begin 时间)。
-
通过
proceed()方法放行 ,此时才真正执行queryBookById的原始业务代码。 -
原始方法执行完毕,返回结果。
-
接着执行我们写的后置逻辑(记录 end 时间并打印日志)。
-
最后,把原始方法的返回值返回给调用方。
AOP 调用时序图

由此推导出了 AOP 的核心优势:
-
代码无侵入:原始方法一行没改。
-
减少重复代码:只写一次切面即可。
-
维护方便:业务方法和增强逻辑完全解耦。
