You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot 3.x中@RestControllerAdvice无法捕获自定义异常的原因及解决方法咨询

Spring Boot 3.x中@RestControllerAdvice无法捕获自定义异常的原因及解决方法咨询

嘿,我之前升级Spring Boot 3.x的时候也踩过类似的坑,结合你给出的代码和已经排查的点,咱们来一步步捋清楚问题所在,大概率是这几个常见的小疏忽:

可能的原因及对应解决方法

1. 注解导包搞混了(最容易踩的坑)

你提到用了jakarta.*的导入,但要注意:@RestControllerAdvice@ExceptionHandler这些是Spring框架专属的注解,根本不在jakarta.*包下!如果你硬去找jakarta版本的这些注解(实际上不存在),那你的GlobalExceptionHandler根本不会被Spring识别成全局异常处理器,自然不会触发。

解决方法
立刻检查你的导入语句,确保这些注解来自Spring的web包:

  • @RestControllerAdvice → 导入org.springframework.web.bind.annotation.RestControllerAdvice
  • @ExceptionHandler → 导入org.springframework.web.bind.annotation.ExceptionHandler
  • ProblemDetail → 导入org.springframework.http.ProblemDetail
  • HttpStatus → 导入org.springframework.http.HttpStatus

至于jakarta.*的导入,只需要用在Servlet相关的API上(比如jakarta.servlet.http.HttpServletRequest),Spring的核心web注解还是得用它自己的包。

2. 被Spring内置异常处理器抢了先

Spring Boot 3.x的默认异常处理链里,有些内置解析器的优先级可能比你的自定义处理器高,导致你的代码还没执行,就被内置处理器先处理了异常。

解决方法
给你的GlobalExceptionHandler加上@Order(Ordered.HIGHEST_PRECEDENCE)注解,把它的优先级拉到最高,确保Spring第一个找它处理异常:

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(MyBusinessException.class)
    public ProblemDetail handleMyBusinessException(MyBusinessException ex) {
        return ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, ex.getMessage());
    }
}

3. ProblemDetail的配置或序列化问题

有时候不是处理器没触发,而是ProblemDetail没有被正确序列化为JSON,导致Spring fallback到了默认的500错误页面。虽然Spring Boot Starter Web默认带了Jackson,但也可能有配置疏漏。

验证&解决方法
先换个简单的返回值测试处理器是否真的生效,比如返回自定义的Map:

@ExceptionHandler(MyBusinessException.class)
public ResponseEntity<Map<String, Object>> handleMyBusinessException(MyBusinessException ex) {
    Map<String, Object> errorMap = new HashMap<>();
    errorMap.put("code", HttpStatus.BAD_REQUEST.value());
    errorMap.put("msg", ex.getMessage());
    return ResponseEntity.badRequest().body(errorMap);
}

如果这个能返回正确的JSON,那说明是ProblemDetail的配置问题,可以去application.yml里确认这俩配置是否开启:

spring:
  mvc:
    problemdetails:
      enabled: true  # Spring Boot 3.x默认开启,但手动确认更稳妥
  jackson:
    serialization:
      fail-on-empty-beans: false  # 避免ProblemDetail序列化失败

4. 组件扫描的隐藏问题

虽然你说包在同一基础包,但还是可以快速验证一下:你的GlobalExceptionHandler是否真的被Spring实例化了?

验证方法
GlobalExceptionHandler加个构造方法打日志,启动项目时看控制台有没有输出:

public GlobalExceptionHandler() {
    System.out.println("全局异常处理器已初始化!");
}

如果启动时没看到这句话,说明这个类没被Spring扫描到,要么是@SpringBootApplication的扫描范围没覆盖到它,要么是某个@ComponentScan注解不小心排除了它,调整启动类的scanBasePackages就能解决:

@SpringBootApplication(scanBasePackages = "com.your.base.package")
public class YourApplication {
    public static void main(String[] args) {
        SpringApplication.run(YourApplication.class, args);
    }
}

最后快速排查步骤

  1. 先确认GlobalExceptionHandler的导包完全正确
  2. 验证它是否被Spring实例化了(用构造方法打日志)
  3. 加上@Order注解提高优先级
  4. 换简单返回值测试处理器是否生效

我之前就是手滑把@RestControllerAdvice导成了不存在的jakarta版本,改回Spring的包后立刻就正常了😅 按照这些步骤排查,应该很快能解决问题!

火山引擎 最新活动