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:
- ✅
@Before
:前置处理,适合做参数校验、日志记录 - ✅
@AfterReturning / @AfterThrowing / @After
:后置处理,适合结果增强、事件发布、资源清理 - ✅
@Around
:全能型选手,适合性能监控、事务控制、缓存管理等复杂场景
所有示例代码均已开源,可在 GitHub 项目中查看完整实现:
👉 https://github.com/eugenp/tutorials/tree/master/spring-aop
掌握这些 Advice 类型的差异和适用场景,能让你在设计通用组件时更加游刃有余。别再到处写重复代码了,是时候把横切逻辑交给 AOP 来处理了!