AOP 是 Spring 核心特性之一,也是面试与日常开发高频使用的技能。它能帮我们无侵入地统一处理日志、权限、耗时统计、异常捕获等通用逻辑,大幅减少重复代码、降低耦合。

本文用最通俗的讲解 + 完整可运行案例,带你彻底掌握 Spring Boot AOP。


一、什么是 AOP?为什么要用?

AOP:Aspect Oriented Programming 面向切面编程

核心思想:
核心业务通用非业务逻辑分离开。

  • 核心业务:用户登录、下单、支付
  • 通用逻辑:日志、鉴权、耗时统计、异常处理、事务

不使用 AOP 的问题:

  • 每个方法都写重复代码
  • 业务代码被污染
  • 修改麻烦,牵一发动全身
  • 耦合度极高

AOP 的好处:

  • 无侵入:不修改业务代码
  • 复用强:一处编写,多处生效
  • 易维护:统一修改,全局生效
  • 职责清晰:业务归业务,切面归切面

二、AOP 核心术语(一看就懂)

  • 切面 Aspect:要插入的通用功能(日志、权限等)
  • 切点 Pointcut:在哪些方法上切入(匹配规则)
  • 通知 Advice:在方法前/后/异常/环绕执行什么逻辑
  • 连接点 JoinPoint:可以被切入的目标方法
  • 织入 Weaving:把切面逻辑插入目标方法的过程
  • 目标对象 Target:被代理的业务类

三、AOP 五大通知类型

  1. @Before:方法执行前
  2. @After:方法执行后(无论是否异常)
  3. @AfterReturning:方法正常返回后
  4. @AfterThrowing:方法抛出异常后
  5. @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 常见使用场景

  • 接口日志统一打印
  • 方法耗时统计
  • 全局权限校验
  • 全局异常捕获包装
  • 多数据源切换
  • 分布式锁、限流、幂等
  • 缓存自动处理
  • 操作日志审计

九、避坑指南(重要)

  1. 同类内部调用不生效(this.method() 不走代理)
  2. private 方法无法切入(必须是 public)
  3. 异常不要在切面吞掉,要抛出或包装
  4. 环绕通知必须调用 proceed()
  5. 避免切点表达式范围过大(如 *.*.*(..))导致性能问题

十、总结

Spring AOP 本质是 动态代理,帮我们无侵入增强方法。

使用三步:

  1. 加 AOP 依赖
  2. 写切点(匹配哪些方法)
  3. 写通知(前/后/环绕 增强逻辑)

AOP 让代码更干净、职责更清晰、维护更轻松,是 Spring 开发必备技能。

来自降重鸟技术团队,请勿转载,降重鸟地址https://jcn2.cn

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐