AOP 是 Spring 核心特性之一,也是面试与日常开发高频使用的技能。它能帮我们无侵入地统一处理日志、权限、耗时统计、异常捕获等通用逻辑,大幅减少重复代码、降低耦合。
本文用最通俗的讲解 + 完整可运行案例,带你彻底掌握 Spring Boot AOP。
一、什么是 AOP?为什么要用?
AOP:Aspect Oriented Programming 面向切面编程。
核心思想:
把核心业务和通用非业务逻辑分离开。
- 核心业务:用户登录、下单、支付
- 通用逻辑:日志、鉴权、耗时统计、异常处理、事务
不使用 AOP 的问题:
- 每个方法都写重复代码
- 业务代码被污染
- 修改麻烦,牵一发动全身
- 耦合度极高
AOP 的好处:
- 无侵入:不修改业务代码
- 复用强:一处编写,多处生效
- 易维护:统一修改,全局生效
- 职责清晰:业务归业务,切面归切面
二、AOP 核心术语(一看就懂)
- 切面 Aspect:要插入的通用功能(日志、权限等)
- 切点 Pointcut:在哪些方法上切入(匹配规则)
- 通知 Advice:在方法前/后/异常/环绕执行什么逻辑
- 连接点 JoinPoint:可以被切入的目标方法
- 织入 Weaving:把切面逻辑插入目标方法的过程
- 目标对象 Target:被代理的业务类
三、AOP 五大通知类型
- @Before:方法执行前
- @After:方法执行后(无论是否异常)
- @AfterReturning:方法正常返回后
- @AfterThrowing:方法抛出异常后
- @Around:环绕通知(最强大,前后都能控制)
执行顺序:
Around 前 → Before → 方法执行 → AfterReturning → After → Around 后四、Spring Boot 快速使用 AOP
1. 引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>2. 编写业务类(目标对象)
UserService:
@Service @Slf4j public class UserService { public void printUserInfo(User user) { log.info("用户ID:{}", user.getId()); log.info("用户名:{}", user.getUsername()); log.info("昵称:{}", user.getNickname()); } }Controller:
@RestController @RequiredArgsConstructor public class UserController { private final UserService userService; @PostMapping("/user") public String print(@RequestBody User user) { userService.printUserInfo(user); return "success"; } }五、编写切面(核心步骤)
@Aspect @Component @Slf4j public class LogAspect { // 1. 定义切点:匹配 UserService 所有方法 @Pointcut("execution(* com.example.aop.service.UserService.*(..))") public void logPointcut() { } // 2. 前置通知 @Before("logPointcut()") public void before() { log.warn("[前置] 方法即将执行"); } // 3. 后置通知 @After("logPointcut()") public void after() { log.warn("[后置] 方法已执行"); } // 4. 返回通知 @AfterReturning("logPointcut()") public void afterReturning() { log.warn("[返回] 方法正常返回"); } // 5. 异常通知 @AfterThrowing("logPointcut()") public void afterThrowing() { log.error("[异常] 方法报错了"); } }切点表达式说明
execution(* com.xxx.service.UserService.*(..)) * 任意返回值 .. 任意参数 * 任意方法名六、环绕通知 @Around(最常用)
环绕通知可以控制目标方法是否执行、修改参数、修改返回值、捕获异常。
@Around("logPointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { log.warn("[环绕前]"); long start = System.currentTimeMillis(); // 执行目标方法 Object result = joinPoint.proceed(); long end = System.currentTimeMillis(); log.warn("[环绕后] 耗时:{}ms", end - start); return result; }⚠️ 注意:
- 必须调用
proceed()才会执行目标方法 - 必须抛出异常或捕获,否则业务异常会被吃掉
七、在通知中获取方法参数
可以在切面中拿到目标方法的入参,常用于日志打印、参数校验。
@Before("logPointcut() && args(user)") public void before(User user) { log.warn("参数:{}", user); }多参数写法:
@Before("logPointcut() && args(user,id)") public void before(User user, Integer id) { ... }八、AOP 常见使用场景
- 接口日志统一打印
- 方法耗时统计
- 全局权限校验
- 全局异常捕获包装
- 多数据源切换
- 分布式锁、限流、幂等
- 缓存自动处理
- 操作日志审计
九、避坑指南(重要)
- 同类内部调用不生效(this.method() 不走代理)
- private 方法无法切入(必须是 public)
- 异常不要在切面吞掉,要抛出或包装
- 环绕通知必须调用 proceed()
- 避免切点表达式范围过大(如
*.*.*(..))导致性能问题
十、总结
Spring AOP 本质是动态代理,帮我们无侵入增强方法。
使用三步:
- 加 AOP 依赖
- 写切点(匹配哪些方法)
- 写通知(前/后/环绕 增强逻辑)
AOP 让代码更干净、职责更清晰、维护更轻松,是 Spring 开发必备技能。
来自降重鸟技术团队,请勿转载,降重鸟地址https://jcn2.cn