关于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 项目都会标配该组件。

相关推荐
一 乐1 小时前
幼儿园管理系统|基于springboot + vue幼儿园管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·幼儿园管理系统
Bat U1 小时前
JavaEE|SpringMVC
java·java-ee
摇滚侠1 小时前
SpringMVC 入门到实战 SpringMVC 的执行流程 96
java·后端·spring·maven·intellij-idea
唐青枫1 小时前
Java Liquibase 实战指南:让数据库变更像代码一样可追踪
java
qq_422152572 小时前
PDF 解密工具怎么选?2026 年文档密码移除方案与注意事项
java·前端·pdf
布朗克1682 小时前
38 Spring Boot入门——自动配置、核心注解与Starter机制
java·spring boot·后端
沪漂阿龙2 小时前
LangChain 系列:Structured Output结构化输出与源码解析
java·人工智能·架构·langchain
半夜燃烧的香烟2 小时前
springboot3.0 集成minio上传文件,支持多个桶名
java·开发语言·spring boot
J2虾虾2 小时前
Android支持Java语言的标准
android·java·开发语言