在 Spring Boot 中,带有 "Advice" 后缀的注解和类确实容易让人混淆。简单来说,它们可以分为两大阵营:面向切面编程(AOP)中的"通知" 和 专门用于增强 Spring MVC 中控制器(Controller)的"通知" 。
为了让你快速建立一个整体印象,下面这个表格清晰地展示了它们的核心区别。
| 特性维度 | AOP 中的 "Advice" (如 @Before, @Around) |
Controller 增强器 @ControllerAdvice |
|---|---|---|
| 所属技术 | AOP(面向切面编程) | Spring MVC(Web层) |
| 作用目标 | 任何 Spring Bean 的方法(如 Service, Repository 的方法) | 仅 @Controller/@RestController中的方法 |
| 核心目的 | 解耦横切关注点(如日志、事务、权限) | 统一处理 Web 控制器的异常、数据、参数 |
| 关键注解/类 | @Before, @After, @Around, @AfterReturning, @AfterThrowing |
@ControllerAdvice, @RestControllerAdvice |
接下来,我们深入了解一下每一类的具体职责和应用。
🛠️ AOP 中的 "Advice"(通知)
AOP 中的通知允许你在目标方法执行的特定"点"注入代码,这些"点"被称为"连接点"。Spring AOP 主要支持五种类型的通知,它们像钩子一样挂在方法调用的不同位置 。
下面的时序图直观地展示了一个方法被AOP增强后,各种通知的执行时机:

下面是这五种通知的详细说明和简单代码示例 :
-
前置通知 (
@Before)- 时机 :在目标方法执行之前运行。
- 应用:常用于参数校验、权限检查等。
less@Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void logBefore(JoinPoint joinPoint) { System.out.println("即将执行方法: " + joinPoint.getSignature().getName()); } } -
后置通知 (
@After)- 时机 :在目标方法执行之后 运行,无论方法是正常返回还是抛出异常。类似于
finally块。 - 应用:常用于资源清理。
less@Aspect @Component public class CleanupAspect { @After("execution(* com.example.service.*.*(..))") public void doCleanup(JoinPoint joinPoint) { System.out.println("方法执行完毕,进行资源清理: " + joinPoint.getSignature().getName()); } } - 时机 :在目标方法执行之后 运行,无论方法是正常返回还是抛出异常。类似于
-
返回通知 (
@AfterReturning)- 时机 :在目标方法成功执行并正常返回后运行。
- 应用:可以访问方法的返回值,常用于记录操作结果。
less@Aspect @Component public class MonitoringAspect { @AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result") public void logAfterReturning(JoinPoint joinPoint, Object result) { System.out.println("方法 " + joinPoint.getSignature().getName() + " 成功返回,结果为: " + result); } } -
异常通知 (
@AfterThrowing)- 时机 :在目标方法抛出异常后运行。
- 应用:用于记录异常日志、告警等。
less@Aspect @Component public class ExceptionHandlingAspect { @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex") public void logAfterThrowing(JoinPoint joinPoint, Exception ex) { System.err.println("方法 " + joinPoint.getSignature().getName() + " 抛出异常: " + ex.getMessage()); } } -
环绕通知 (
@Around)- 时机 :最强大的通知,它包围了整个方法调用。可以手动决定是否执行目标方法,以及如何处理方法的返回值和异常。
- 应用:性能监控、缓存、事务管理等。
java@Aspect @Component public class PerformanceAspect { @Around("execution(* com.example.service.*.*(..))") public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); try { // 执行目标方法 return joinPoint.proceed(); } finally { long duration = System.currentTimeMillis() - start; System.out.println("方法 " + joinPoint.getSignature().getName() + " 执行耗时: " + duration + "ms"); } } }
🌐 Controller 增强器 @ControllerAdvice
@ControllerAdvice(及其组合注解 @RestControllerAdvice)是一个专门为 Spring MVC 控制器 设计的增强器。它本身不是基于 AOP 的代理机制,而是 Spring MVC 框架提供的一种内置功能,用于全局性地处理所有控制器的通用逻辑 。它主要与三个注解组合来实现三大功能:
-
全局异常处理 (
@ExceptionHandler)这是
@ControllerAdvice最常用的功能。你可以在一个地方集中处理所有控制器中抛出的异常,返回统一的 JSON 错误格式或错误页面,避免在每个控制器中重复编写 try-catch。kotlin@RestControllerAdvice // 等同于 @ControllerAdvice + @ResponseBody public class GlobalExceptionHandler { @ExceptionHandler(BizException.class) public Result handleBizException(BizException e) { // 处理自定义业务异常 return Result.error(e.getCode(), e.getMessage()); } @ExceptionHandler(Exception.class) public Result handleException(Exception e) { // 处理其他所有未精确定义的异常 return Result.error("500", "系统繁忙"); } } -
全局数据绑定 (
@ModelAttribute)这个方法返回的数据会自动添加到所有控制器的 Model 中,使得在每个视图(如 JSP、Thymeleaf 模板)或控制器方法中都能获取到这些公共数据。
typescript@ControllerAdvice public class GlobalDataBinder { @ModelAttribute("globalData") public Map<String, Object> globalData() { Map<String, Object> map = new HashMap<>(); map.put("siteName", "我的应用"); map.put("currentYear", Calendar.getInstance().get(Calendar.YEAR)); return map; } } -
全局数据预处理 (
@InitBinder)用于全局定制请求参数绑定的规则,例如设置日期格式、忽略特定字段等。
typescript@ControllerAdvice public class GlobalDataPreAdvice { @InitBinder public void handleInitBinder(WebDataBinder dataBinder) { // 设置日期格式 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); } }此外,
@ControllerAdvice可以通过basePackages,annotations,assignableTypes等属性来限制其作用范围,只增强特定的控制器 。
💡 如何选择?
- 当你需要对业务方法 (如 Service 层的方法)添加日志、事务、权限等横切关注点 时,使用 AOP 的通知 (如
@Around)。 - 当你需要对 Web 控制器 进行统一的异常处理、数据绑定或参数预处理 时,使用
@ControllerAdvice。