关于Spring MVC 基于 AOP 实现的全局控制器统一处理方案@ControllerAdvice

在 Spring 框架中 @ControllerAdvice 直译就是控制器增强注解 ,是 Spring MVC 基于 AOP 实现的全局控制器统一处理方案,也是日常开发的高频注解。

一、基础概述

1. 基本信息

  • 所属包org.springframework.web.bind.annotation.ControllerAdvice
  • 版本要求:Spring MVC 3.2+ 开始支持
  • 核心作用 :对项目中所有标注 @Controller / @RestController 的控制器做全局统一增强,抽离通用逻辑,避免重复代码。
  • 本质:全局切面,拦截所有控制器的请求流程。

2. 衍生注解(重点区分)

java 复制代码
​
// 组合注解 = @ControllerAdvice + @ResponseBody
@RestControllerAdvice

​
  • @ControllerAdvice :默认用于页面跳转 / 视图渲染 ,返回页面;若要返回 JSON,方法需额外加 @ResponseBody
  • @RestControllerAdvice :前后端分离项目首选,天然返回 JSON 格式,接口开发几乎都用它。

二、三大核心功能(搭配不同注解使用)

@ControllerAdvice 本身不单独工作,需要配合以下三个注解实现能力,也是实际开发最常用的三大场景。

场景 1:全局异常处理(⭐ 最常用)

搭配 @ExceptionHandler ,统一捕获所有控制器抛出的异常,统一响应格式、统一异常提示 ,替代每个接口写 try-catch

1. 第一步:定义统一返回实体

项目标准接口返回格式:

java 复制代码
​
import lombok.Data;

@Data
public class Result<T> {
    // 响应码:200成功,500系统异常,自定义码区分业务异常
    private Integer code;
    // 响应信息
    private String msg;
    // 响应数据
    private T data;

    // 成功响应
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMsg("请求成功");
        result.setData(data);
        return result;
    }

    // 失败响应
    public static <T> Result<T> fail(Integer code, String msg) {
        Result<T> result = new Result<>();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }
}

​

2. 第二步:全局异常处理器(核心代码)

java 复制代码
​
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

// 全局控制器增强 + 统一返回JSON
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 1. 捕获【空指针异常】(精准异常,优先级更高)
    @ExceptionHandler(NullPointerException.class)
    public Result<Void> handleNullPoint() {
        return Result.fail(500, "数据为空,操作失败");
    }

    // 2. 捕获【所有运行时异常】
    @ExceptionHandler(RuntimeException.class)
    public Result<Void> handleRuntimeException(RuntimeException e) {
        // 可打印日志:log.error("运行时异常:", e);
        return Result.fail(500, "业务异常:" + e.getMessage());
    }

    // 3. 兜底:捕获【所有Exception异常】(父类异常,最后匹配)
    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(Exception e) {
        return Result.fail(500, "系统繁忙,请稍后重试");
    }
}

​

规则说明

  1. 异常优先级具体子类异常 > 父类异常(如上示例:空指针会优先被第一个方法捕获)。
  2. 局部优先 :如果某个 Controller 内部单独写了 @ExceptionHandler,会优先使用局部异常处理,全局失效。
  3. 仅捕获控制器层抛出的异常:Service/DAO 层异常需要向上抛到 Controller 才能被拦截。

场景 2:全局请求参数预处理

搭配 @InitBinder ,全局统一处理表单 / URL 拼接参数,例如:字符串去首尾空格、全局日期格式转换、参数权限过滤等。

注意:该注解对 JSON 请求体(@RequestBody)不生效,JSON 参数转换需使用全局消息转换器。

java 复制代码
​
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalParamBinder {

    // 全局参数绑定预处理
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        // 示例1:所有字符串参数自动去除首尾空格
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));

        // 示例2:禁止绑定指定字段(防止前端篡改敏感字段)
        binder.setDisallowedFields("createTime", "updateTime");
    }
}

​

场景 3:全局绑定公共数据

搭配 @ModelAttribute ,在所有接口执行前 ,自动向 Model 中注入公共数据,所有 Controller 都能直接获取。

多用于页面渲染场景,前后端分离接口开发使用较少。

java 复制代码
​
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalModelData {

    // 所有接口执行前,自动往Model存入数据
    @ModelAttribute
    public void addCommonData(Model model) {
        model.addAttribute("projectName", "测试项目");
        model.addAttribute("version", "1.0.0");
    }
}

​

三、限定生效范围(常用属性)

默认 @ControllerAdvice全局所有控制器生效,可通过属性限定范围,精准控制作用域:

java 复制代码
​
// 1. 指定包路径:只对 com.example.controller 包下的控制器生效
@RestControllerAdvice(basePackages = "com.example.controller")

// 2. 指定类:该类所在包及子包下的控制器生效
@RestControllerAdvice(basePackageClasses = UserController.class)

// 3. 指定注解:只对标记了 @RestController 的类生效
@RestControllerAdvice(annotations = RestController.class)

​

四、常见踩坑 & 注意事项

  1. JSON 参数无法被 @InitBinder 处理 @InitBinder 只处理 form表单/URL参数@RequestBody 接收的 JSON 参数需要自定义 HttpMessageConverter 做统一转换。

  2. 不要捕获 Throwable``Throwable 包含 Error(虚拟机级错误:内存溢出、栈溢出),捕获后会掩盖严重系统问题,一般只捕获 Exception

  3. 过滤器 / 拦截器异常不生效@ControllerAdvice 作用于控制器层FilterInterceptor 抛出的异常不会被拦截。

  4. 顺序问题全局异常处理器建议单独建类,不要和业务 Controller 写在一起,保证全局统一。


五、总结(开发选型)

注解 使用场景
@ControllerAdvice 传统 MVC 项目(返回页面、视图)
@RestControllerAdvice 前后端分离接口项目(主流),统一异常、统一 JSON 返回

在企业级项目中,全局异常处理@RestControllerAdvice 最核心的用法,几乎所有 Spring Boot 项目都会标配该组件。

相关推荐
karry_k20 小时前
MyBatis批量insert-select踩坑:useGeneratedKeys=true 可能让PostgreSQL返回大量插入结果
java·后端
karry_k20 小时前
PostgreSQL 在 MyBatis 中执行正常 SQL 失效:一次 DELETE USING 踩坑记录
java·后端
SamDeepThinking1 天前
从源码到代码:MyBatis-Flex 与 MyBatis-Plus 的逐项对比
java·后端·程序员
她的男孩1 天前
Spring Boot 接 Flowable 工作流:用 3 个注解搭一个请假审批流程
java·后端·架构
荣码1 天前
LLM结构化输出:让AI返回JSON而不是废话,我踩了4个坑
java·python
plainGeekDev1 天前
Gson → kotlinx.serialization
android·java·kotlin
小bo波2 天前
Java Swing 图形用户界面实验 —— 从算术练习到游戏开发的完整实践
java·课程设计·gui·游戏开发·扫雷·swing
咖啡八杯2 天前
GoF设计模式——备忘录模式
java·后端·spring·设计模式