1. 概述

本文将深入介绍 Spring AOP 中支持的几种 Advice(通知)类型。

在 AOP 编程中,Advice 是指切面(Aspect)在特定连接点(Join Point)上执行的动作。常见的 Advice 类型包括:前置通知(Before)后置通知(After)、以及功能最强大的 环绕通知(Around)。这些机制的核心用途是解耦横切关注点(cross-cutting concerns),比如日志记录、性能监控、缓存控制和事务管理等。

如果你对切点(Pointcut)表达式还不熟悉,建议先阅读我们之前发布的 Spring AOP 切点教程


2. 启用 AOP 支持

在 Spring 中使用注解方式定义切面时,必须先启用 AspectJ 自动代理支持。

2.1 配置类中启用

需要在配置类上添加 @EnableAspectJAutoProxy 注解:

@Configuration
@EnableAspectJAutoProxy
public class AopConfiguration {
    // 配置内容
}

✅ 这一步是必须的,否则你的 @Aspect 类不会被识别为切面。

2.2 Spring Boot 项目中的情况

在 Spring Boot 项目中,**无需手动添加 @EnableAspectJAutoProxy**。只要 classpath 下存在 AspectJ 相关依赖(如 spring-aspects),Spring Boot 的自动配置类 AopAutoConfiguration 就会自动开启 AOP 支持。

⚠️ 踩坑提醒:如果你发现切面不生效,优先检查是否引入了 spring-boot-starter-aop 或相关依赖。


3. 前置通知(Before Advice)

前置通知通过 @Before 注解声明,在目标方法执行前触发。它不能阻止方法执行,除非抛出异常。

下面是一个典型的日志切面示例,用于记录所有 Repository 层方法的调用:

@Component
@Aspect
public class LoggingAspect {

    private Logger logger = Logger.getLogger(LoggingAspect.class.getName());

    @Pointcut("@target(org.springframework.stereotype.Repository)")
    public void repositoryMethods() {};

    @Before("repositoryMethods()")
    public void logMethodCall(JoinPoint jp) {
        String methodName = jp.getSignature().getName();
        logger.info("Before " + methodName);
    }
}

📌 关键点说明:

  • @Pointcut 定义了一个切点,匹配所有被 @Repository 注解的类中的方法
  • @Before 绑定该切点,实现前置拦截
  • JoinPoint 参数可用于获取方法签名、参数等上下文信息

这个切面会在每个 Repository 方法调用前输出日志,简单粗暴但非常实用。


4. 后置通知(After Advice)

后置通知有三种变体,分别对应不同的执行时机:

类型 注解 触发条件
最终通知 @After 方法执行完毕后(无论成功或抛异常)
返回通知 @AfterReturning 方法正常返回后
异常通知 @AfterThrowing 方法抛出异常后

4.1 使用场景:发布领域事件

假设我们希望在某个实体(如 Foo)创建成功后发布一个事件,但又不想让 DAO 层承担额外职责(避免违反单一职责原则),就可以用 @AfterReturning 实现解耦。

@Component
@Aspect
public class PublishingAspect {

    private ApplicationEventPublisher eventPublisher;

    @Autowired
    public void setEventPublisher(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Pointcut("@target(org.springframework.stereotype.Repository)")
    public void repositoryMethods() {}

    @Pointcut("execution(* *..create*(Long,..))")
    public void firstLongParamMethods() {}

    @Pointcut("repositoryMethods() && firstLongParamMethods()")
    public void entityCreationMethods() {}

    @AfterReturning(value = "entityCreationMethods()", returning = "entity")
    public void logMethodCall(JoinPoint jp, Object entity) throws Throwable {
        eventPublisher.publishEvent(new FooCreationEvent(entity));
    }
}

✅ 亮点解析:

  • 使用复合切点精准定位“Repository 中以 Long 为第一个参数的 createXXX 方法”
  • returning = "entity" 允许我们捕获方法返回值,直接作为事件数据
  • 解耦了业务操作与事件发布逻辑

配套的事件监听器如下:

@Component
public class FooCreationEventListener implements ApplicationListener<FooCreationEvent> {

    private Logger logger = Logger.getLogger(getClass().getName());

    @Override
    public void onApplicationEvent(FooCreationEvent event) {
        logger.info("Created foo instance: " + event.getSource().toString());
    }
}

这样就实现了“创建完成自动通知”的轻量级事件驱动架构。


5. 环绕通知(Around Advice)

环绕通知是 AOP 中最强大的一种类型,由 @Around 注解声明。它可以完全包裹目标方法的执行过程,允许你在方法调用前后都插入自定义逻辑,并决定是否继续执行原方法。

典型应用场景:方法执行耗时统计

@Aspect
@Component
public class PerformanceAspect {

    private Logger logger = Logger.getLogger(getClass().getName());

    @Pointcut("within(@org.springframework.stereotype.Repository *)")
    public void repositoryClassMethods() {};

    @Around("repositoryClassMethods()")
    public Object measureMethodExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        Object retval = pjp.proceed();  // 继续执行目标方法
        long end = System.nanoTime();
        String methodName = pjp.getSignature().getName();
        logger.info("Execution of " + methodName + " took " + 
          TimeUnit.NANOSECONDS.toMillis(end - start) + " ms");
        return retval;
    }
}

⚠️ 使用 Around Advice 的注意事项:

  • 参数必须是 ProceedingJoinPoint,否则无法调用 proceed()
  • 必须显式调用 proceed(),否则目标方法不会执行
  • 可以选择不调用 proceed() 实现“短路”逻辑(比如缓存命中时不查数据库)
  • 返回类型应为 Object,以兼容各种返回值类型的方法

✅ 这个性能监控切面非常实用,只需加一个注解就能对整个 Repository 层进行埋点,适合集成到通用组件中。


6. 总结

本文系统介绍了 Spring AOP 中的三类核心 Advice:

  1. @Before:前置处理,适合做参数校验、日志记录
  2. @AfterReturning / @AfterThrowing / @After:后置处理,适合结果增强、事件发布、资源清理
  3. @Around:全能型选手,适合性能监控、事务控制、缓存管理等复杂场景

所有示例代码均已开源,可在 GitHub 项目中查看完整实现:
👉 https://github.com/eugenp/tutorials/tree/master/spring-aop

掌握这些 Advice 类型的差异和适用场景,能让你在设计通用组件时更加游刃有余。别再到处写重复代码了,是时候把横切逻辑交给 AOP 来处理了!


原始标题:Introduction to Advice Types in Spring | Baeldung