You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@ControllerAdvice与校验异常处理的区别、作用及使用方法咨询

@ControllerAdvice与校验异常处理:区别、作用及工作原理解析

问题背景

我在项目中用校验机制处理异常,了解到@ControllerAdvice也能做异常处理,但搞不懂它的作用、和校验异常处理的差异,以及怎么用,希望有人讲下它的工作原理。

校验机制代码示例:

@RestController
@RequestMapping("/api")
@Validated
public class UserController {

    @Autowired
    UserRepository userrepo;
    
    @PostMapping(value="/users")
    ResponseEntity<?> create( @Valid @RequestBody User user) {
        
        User addeduser = userrepo.save(user);
        URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                            .path("/{id}")
                            .buildAndExpand(addeduser.getId())
                            .toUri();
        
        return ResponseEntity.created(location).build();
    }
}

@ControllerAdvice代码示例:

@ControllerAdvice
public class GlobalResponseException {
    @ExceptionHandler(MyException.class)
    public void handleMyException() {}
}

一、@ControllerAdvice的核心作用

简单说,@ControllerAdvice是Spring MVC提供的全局增强器,针对异常处理场景,它的核心价值是:

  • 统一捕获全应用内抛出的指定异常,不用在每个Controller里重复写异常处理逻辑
  • 可以自定义所有异常的响应格式,让前后端交互的错误信息更规范
  • 除了异常处理,还能实现全局数据绑定、全局请求参数预处理(不过你主要关注异常,这俩就不多提)

二、和校验异常处理的核心差异

你用的@Valid/@Validated属于JSR-380校验规范,触发校验后会抛出MethodArgumentNotValidException(请求体校验)或ConstraintViolationException(路径/请求参数校验)这类异常。它和@ControllerAdvice的差异主要在这几点:

1. 作用范围

  • 原生校验异常处理:如果不额外配置,要么依赖Spring默认的错误响应,要么得在单个Controller里写@ExceptionHandler处理,只能覆盖当前Controller的异常
  • @ControllerAdvice:全局生效,所有Controller抛出的匹配异常都会被捕获,一次配置全应用复用

2. 处理的异常类型

  • 校验异常处理:只能处理和校验相关的特定异常
  • @ControllerAdvice:可以处理任意类型的异常——自定义业务异常(比如你示例里的MyException)、系统异常、第三方服务调用异常等,覆盖场景更广

3. 代码复用性

  • 原生校验处理:每个需要校验的Controller都要写重复的异常处理代码,冗余度高
  • @ControllerAdvice:只需要写一次异常处理逻辑,全应用所有Controller都能复用,代码更简洁易维护

三、@ControllerAdvice的工作原理

Spring容器启动时,会自动扫描并注册带有@ControllerAdvice的类,这个类相当于一个全局的异常拦截器,流程是:

  1. 当Controller方法抛出异常时,Spring MVC的DispatcherServlet会遍历所有注册的@ControllerAdvice类
  2. 匹配每个类里的@ExceptionHandler方法,根据抛出的异常类型找到最匹配的处理方法(比如子类异常优先匹配对应的方法,父类异常兜底)
  3. 执行匹配到的处理方法,返回你自定义的响应(比如结构化的错误JSON、跳转错误页面等)

四、结合校验异常的实战用法

针对你给出的校验代码,我们可以用@ControllerAdvice统一处理校验异常,返回更友好的错误信息:

// 用@RestControllerAdvice更适合REST接口,默认返回JSON,不用每个方法加@ResponseBody
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 处理请求体参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, String>> handleValidationErrors(MethodArgumentNotValidException ex) {
        Map<String, String> errorMap = new HashMap<>();
        // 遍历所有校验错误,提取字段名和错误信息
        ex.getBindingResult().getAllErrors().forEach(error -> {
            String fieldName = ((FieldError) error).getField();
            String errorMsg = error.getDefaultMessage();
            errorMap.put(fieldName, errorMsg);
        });
        // 返回400错误和结构化错误信息
        return ResponseEntity.badRequest().body(errorMap);
    }

    // 处理自定义业务异常
    @ExceptionHandler(MyException.class)
    public ResponseEntity<String> handleCustomException(MyException ex) {
        // 返回500错误和自定义异常信息
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage());
    }
}

这样,当你调用/api/users接口,请求体的User对象校验不通过时,会返回类似这样的JSON:

{
  "username": "用户名不能为空",
  "password": "密码长度不能少于6位"
}

比Spring默认的错误响应清晰得多,前端也更容易解析。


内容的提问来源于stack exchange,提问作者anderson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 18:50:26