跳到正文
Joeplover
学习笔记·2026-01-17·约 3 分钟阅读

Spring AOP 全面学习:切面编程的 6 种通知类型

Spring AOP 完整学习项目,覆盖 @Before/@After/@AfterReturning/@AfterThrowing/@Around 通知和自定义注解切面。

代码与编程 — Spring AOP 切面编程

Spring AOP 全面学习:6 种通知类型与自调用陷阱

通知类型一览

通知注解触发时机典型用途
前置通知@Before方法执行前参数校验、权限检查
后置通知@After方法执行后(无论成功或异常)资源清理
返回通知@AfterReturning方法成功返回后成功日志、数据脱敏
异常通知@AfterThrowing方法抛出异常后异常告警、事务回滚记录
环绕通知@Around方法执行前后,完全控制性能监控、缓存、重试
引入通知@DeclareParents类加载时动态引入新接口

切入点表达式

@Pointcut("execution(public * com.example.service.*.*(..))")
private void serviceLayer() {}
表达式匹配说明
execution(* com.example.service.*.*(..))service 包下所有类的所有方法最常用
execution(* com.example..*.*(..))com.example 包及其子包递归匹配
within(com.example.service.*)service 包下的所有类类级别匹配
@annotation(com.example.annotation.Monitor)标注了 @Monitor 的方法注解驱动
bean(userService)名为 userService 的 Bean按名称匹配

组合使用

@Around("execution(* com.example.service.*.*(..)) && @annotation(monitor)")
public Object monitor(ProceedingJoinPoint pjp, Monitor monitor) throws Throwable {
    // 只拦截 service 包下标注了 @Monitor 的方法
}

自调用陷阱(最容易被忽略的坑)

@Service
public class UserService {
    
    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
    }
    
    public void batchCreate(List<User> users) {
        for (User user : users) {
            this.createUser(user);  // @Transactional 不生效!
        }
    }
}

AOP 的底层原理是代理。Spring 创建了 UserService 的代理对象,但 this 指向的是原始对象,不是代理对象。所以通过 this.createUser() 调用时不会触发 AOP 增强。

解决方案

方案一:注入自身代理(推荐)

@Service
public class UserService {
    @Autowired
    private UserService self;  // 注入的是代理对象
    
    public void batchCreate(List<User> users) {
        for (User user : users) {
            self.createUser(user);  // 通过代理调用,AOP 生效
        }
    }
}

方案二:AopContext.currentProxy()

@Service
public class UserService {
    public void batchCreate(List<User> users) {
        UserService proxy = (UserService) AopContext.currentProxy();
        for (User user : users) {
            proxy.createUser(user);
        }
    }
}

需要配置 @EnableAspectJAutoProxy(exposeProxy = true)。

方案三:提取到独立的 Service

@Service
public class UserService {
    @Autowired
    private UserCreationService creationService;  // 另一个 Bean
    
    public void batchCreate(List<User> users) {
        for (User user : users) {
            creationService.createUser(user);  // 跨 Bean 调用,AOP 生效
        }
    }
}

实际应用

在我的项目中,AOP 的主要落地场景:

  1. 接口耗时监控
@Around("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public Object monitorApi(ProceedingJoinPoint pjp) throws Throwable {
    long start = System.currentTimeMillis();
    try {
        return pjp.proceed();
    } finally {
        long elapsed = System.currentTimeMillis() - start;
        if (elapsed > 1000) {
            log.warn("慢接口: {} 耗时 {}ms", pjp.getSignature(), elapsed);
        }
    }
}
  1. @Cacheable 注解缓存:Spring 自带的缓存注解基于 AOP
  2. 自定义 @AuditLog 操作日志:记录增删改操作的用户和参数

代理机制

Spring AOP 使用两种代理:

  • JDK 动态代理:目标类实现了接口时使用。代理接口方法。
  • CGLIB 代理:目标类没有接口时使用。生成子类代理。

注意:CGLIB 代理无法代理 final 方法和 private 方法。Spring Boot 2.0+ 默认使用 CGLIB。