全局异常捕获
导言
上图展示的是杭州市民卡APP在2026年1月1日出现的一次典型错误案例:由于埋点功能的异常,不仅导致了主流程中断,还将详细的异常堆栈信息暴露给了用户。这种现象不仅影响了用户体验,也存在安全隐患。如何规范地进行全局异常处理,将在下文详细介绍。
全局异常捕获是现代 Java Web 项目架构不可或缺的一环。它能够集中管理和处理系统中可能出现的各类异常,提升 API 的健壮性、可维护性和用户友好性。通过合理设计全局异常处理器,不仅可以保证前后端接口的响应一致性,还能方便排查和定位问题。
在日常开发中,直接使用 throw e 等方式抛出异常,容易导致大量堆栈信息暴露给调用方,既影响API友好度,也可能带来安全隐患。通过统一的全局异常捕获机制,可以避免异常堆栈泄露,将异常拦截后转化为一致、规范的前端响应。同时,全局异常捕获器还可以作为系统兜底保护——当代码中未被显式捕获的异常发生时,拦截这些异常,返回规范化错误信息,防止系统崩溃或异常信息外泄。
本篇笔记将系统梳理 Spring Boot/Web 项目中常见异常的类别、触发场景和最佳捕获实践。从基础的 @RestControllerAdvice、@ExceptionHandler 的应用,到进阶的各类异常类型归纳,以及推荐的全局异常处理器骨架代码,让项目异常处理更规范、更易扩展。
全局异常捕获
核心注解@RestControllerAdvice和@ExceptionHandler
1 | |
最佳实践
一般统一的全局异常捕获作为一个公共类BaseExceptionHandler,具体微服务中需要引入这个公共类,并定义一个GlobalExceptionHandler继承他。
BaseExceptionHandler 公共父类示例
1 | |
各微服务中的继承实现
1 | |
RestControllerAdvice
@RestControllerAdvice 是 Spring 中用于全局异常处理的注解。它是 @ControllerAdvice 和 @ResponseBody 的组合注解,作用如下:
- 拦截所有被
@RestController注解的控制器中的异常,实现统一的异常处理逻辑。 - 标注在类上后,该类中用
@ExceptionHandler注解的方法会自动处理全局范围内的异常,并返回 JSON 数据(即返回体会被自动序列化为 JSON)。 - 通常用于RESTful 接口项目,提升异常处理的统一性与规范性。
总结:@RestControllerAdvice 可以让你把所有 Controller 层的异常集中处理,并且方便地返回统一的数据结构(如通用的错误响应对象)。
ExceptionHandler
@ExceptionHandler 是 Spring 框架提供的异常处理注解,用于定义在某个方法上,当指定类型的异常在 Controller 层被抛出时,由被注解的方法进行处理。
- 使用方式:注解在方法上,参数为需要捕获的异常类型。可以指定一个或多个异常类。
- 作用:捕获并处理指定异常,通常结合全局异常处理类(如上文中的
@RestControllerAdvice)一起使用,实现异常捕获和统一返回响应。
示例说明:
1 | |
- 当控制器方法执行发生
NullPointerException,该方法会被自动调用进行处理。 - 此方法统一包装 HTTP 状态码与异常信息返回给客户端。
总结:@ExceptionHandler 结合 @RestControllerAdvice 使用,实现了捕获全局 Controller 异常、集中处理和统一返回格式的目的。
常用异常类型
一般来说,在公共类中尽可能抽象地通用捕获一些常用异常类型,例如HttpRequestMethodNotSupportedException,BindException,MethodArgumentNotValidException,HandlerMethodValidationException等
常用异常类型
| 异常类 | 触发场景 | 建议 HTTP 状态码 |
|---|---|---|
MethodArgumentNotValidException |
JSR-303/380 校验失败(@Valid + 对象参数) |
400 Bad Request |
BindException |
表单提交(application/x-www-form-urlencoded)校验失败 | 400 Bad Request |
ConstraintViolationException |
手动调用 Validator.validate() 或路径变量/请求参数校验失败 |
400 Bad Request |
MissingServletRequestParameterException |
缺少必需的请求参数(@RequestParam(required = true)) |
400 Bad Request |
ServletRequestBindingException |
请求参数绑定失败(如类型不匹配) | 400 Bad Request |
HandlerMethodValidationException (Spring6) |
方法级 @Validated 校验失败 |
400 Bad Request |
HttpRequestMethodNotSupportedException |
请求方法不支持(如 POST 访问只允许 GET 的接口) | 405 Method Not Allowed |
NoHandlerFoundException |
404 路径未找到(需开启 spring.mvc.throw-exception-if-no-handler-found=true) |
404 Not Found |
HttpMediaTypeNotSupportedException |
请求头 Content-Type 不支持(如接口要求 application/json,但传了 text/plain) | 415 Unsupported Media Type |
HttpMessageNotReadableException |
JSON 解析失败(如格式错误、字段类型不匹配) | 400 Bad Request |
HttpMessageNotWritableException |
响应序列化失败(通常为服务器编码问题,较少见) | 500 Internal Server Error |
MissingPathVariableException |
@PathVariable 变量缺失(路径模板与注解不匹配) |
500(开发问题)或 400 |
MissingRequestHeaderException |
缺少必需的请求头(@RequestHeader(required = true)) |
400 Bad Request |
TypeMismatchException |
路径变量/请求参数类型转换失败(如字符串转数字失败) | 400 Bad Request |
AsyncRequestTimeoutException |
异步请求超时(如 DeferredResult、Callable 使用时) |
503 Service Unavailable (推荐) |
💡 说明:
- 部分异常(如 HandlerMethodValidationException)为 Spring6+ 新增,用于方法级参数校验。
- 默认 404 不会抛异常,要在
application.yml中开启spring.mvc.throw-exception-if-no-handler-found=true才能被全局异常处理器捕获。 - 500 主要用于服务器端开发失误产生的异常,一般应避免暴露给前端用户。
附配置示例(可捕获 404 路由异常):
1 | |
一、参数校验 & 数据绑定类异常
| 异常类 | 触发场景 | 建议 HTTP 状态码 |
|---|---|---|
MethodArgumentNotValidException |
JSR-303/380 校验失败(@Valid + 对象参数) |
400 Bad Request |
BindException |
表单提交(application/x-www-form-urlencoded)校验失败 |
400 Bad Request |
ConstraintViolationException |
手动调用 Validator.validate() 或路径变量/请求参数校验失败 |
400 Bad Request |
MissingServletRequestParameterException |
缺少必需的请求参数(@RequestParam(required = true)) |
400 Bad Request |
ServletRequestBindingException |
请求参数绑定失败(如类型不匹配) | 400 Bad Request |
💡 注意:
MethodArgumentNotValidException和BindException都继承自BindException,但通常分开处理以获取不同信息。- Spring 6 引入了
HandlerMethodValidationException(用于方法级@Validated),可作为补充。
二、HTTP 方法 & 路由类异常
| 异常类 | 触发场景 | 建议状态码 |
|---|---|---|
HttpRequestMethodNotSupportedException |
请求方法不支持(如 POST 访问只允许 GET 的接口) | 405 Method Not Allowed |
NoHandlerFoundException |
404 路径未找到(需开启 spring.mvc.throw-exception-if-no-handler-found=true) |
404 Not Found |
⚠️ 默认情况下,Spring Boot 对 404 返回 Whitelabel Error Page,不会抛出异常。
若想在 @ControllerAdvice 中捕获 404,必须配置:
1 | |
三、消息转换 & 内容协商异常
| 异常类 | 触发场景 | 建议状态码 |
|---|---|---|
HttpMediaTypeNotSupportedException |
请求头 Content-Type 不支持(如接口要求 application/json,但传 text/plain) | 415 Unsupported Media Type |
HttpMessageNotReadableException |
JSON 解析失败(如格式错误、字段类型不匹配) | 400 Bad Request |
HttpMessageNotWritableException |
序列化响应失败(较少见) | 500 Internal Server Error |
四、路径变量 & 请求头异常
| 异常类 | 触发场景 | 建议状态码 |
|---|---|---|
MissingPathVariableException |
@PathVariable 变量缺失(路径模板与注解不匹配) |
500(开发问题)或 400 |
MissingRequestHeaderException |
缺少必需的请求头(@RequestHeader(required = true)) |
400 Bad Request |
TypeMismatchException |
路径变量/请求参数类型转换失败(如字符串转数字失败) | 400 Bad Request |
五、异步 & 超时异常(高级)
| 异常类 | 触发场景 | 建议状态码 |
|---|---|---|
AsyncRequestTimeoutException |
异步请求超时(使用 DeferredResult 或 Callable) |
503 Service Unavailable |
六、推荐的全局异常处理器骨架
1 | |