前置关联说明在《SpringMVC定制化配置全解》中我们完成了视图适配、拦截器、跨域、JSON序列化、国际化等请求链路定制覆盖了请求入参、拦截、响应格式化场景。本文将独立深度拆解SpringMVC 全局统一异常处理补齐MVC请求闭环最后一环。一、全局统一异常处理的核心意义SpringMVC 原生异常机制存在明显的生产缺陷未做统一处理时项目会出现接口返回杂乱、代码冗余、安全泄露、运维困难等问题也是企业开发强制统一规范的核心原因。原生缺陷汇总响应格式不统一业务异常、代码异常、参数异常返回结构完全不同前端需要多套逻辑适配维护成本极高。业务代码严重冗余每个 Controller、Service 都需要手动 try-catch异常处理逻辑与业务逻辑高度耦合。存在系统安全泄露风险默认异常会直接返回堆栈信息、类路径、服务器报错详情暴露项目结构与源码信息。异常无法统一管控异常散乱抛出无法实现统一日志打印、统一告警、统一降级提示。用户体验差原生 500、404、400 错误无友好中文提示无法适配业务场景。统一异常处理核心价值实现异常解耦、格式统一、安全脱敏、日志可追溯、运维可监控是前后端分离、微服务、中台项目的基础核心基建。二、SpringMVC 异常处理完整执行流程本节细化完整请求异常流转链路从请求接入到异常捕获、解析、封装、响应返回全流程拆解是理解底层原理的核心。2.1 整体执行链路客户端发起请求 → Tomcat 容器接收请求 → DispatcherServlet 前端控制器分发请求 → 拦截器 preHandle 预处理 → Controller 处理器执行 → Service 业务逻辑执行 →代码抛出异常→ DispatcherServlet 捕获全局异常 → 委派 HandlerExceptionResolver 解析器链处理 → 匹配自定义全局异常处理器 → 异常信息脱敏、封装标准化结果 → 输出 JSON 响应给前端 → 拦截器 afterCompletion 回调收尾 → 请求结束2.2 异常匹配核心流程规则异常抛出后不会直接返回由DispatcherServlet 统一拦截终止异常向外冒泡遍历 Spring 内置异常解析器链按照优先级从高到低匹配对应异常处理器精准异常处理器优先匹配业务异常 参数异常 请求异常 通用兜底异常匹配成功后直接执行对应处理逻辑终止后续解析器遍历所有异常统一封装为自定义响应体屏蔽原生异常信息实现安全脱敏。三、底层实现原理Spring 官方规范3.1 核心核心组件SpringMVC 所有异常处理均基于HandlerExceptionResolver异常解析器接口实现框架默认内置三级解析器优先级由高到低ExceptionHandlerExceptionResolver最高优先级专门处理ExceptionHandler注解方法是全局异常处理的核心底层支撑。ResponseStatusExceptionResolver处理标记了ResponseStatus注解的自定义状态码异常。DefaultHandlerExceptionResolver兜底最低优先级框架默认兜底处理 404、405、400 等原生 Web 异常。3.2 全局异常注解原理RestControllerAdvice全局控制器增强注解SpringMVC 提供的全局切面能力仅作用于所有 RestController不依赖 AOP 动态代理不侵入业务代码。工作机制项目启动时自动扫描所有被ExceptionHandler标记的方法构建异常匹配映射表请求异常时自动根据异常类型精准匹配对应处理方法。3.3 优先级终版规则自定义精准异常处理方法 通用兜底异常方法 Spring 内置默认异常解析器四、生产级核心实战代码整套方案包含统一响应实体、自定义业务异常、全局异常处理器、多类型异常捕获、日志记录、安全脱敏、测试接口零改造可直接用于生产环境。4.1 全局统一响应实体import lombok.Data; /** * 全局统一响应实体 * 统一成功/异常返回格式前后端通用约定 */ Data public class ResultT { // 响应状态码200成功4xx客户端异常5xx服务端异常 private Integer code; // 响应提示信息 private String msg; // 响应业务数据 private T data; // 响应时间戳 private long timestamp; /** * 成功响应 */ public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMsg(操作成功); result.setData(data); result.setTimestamp(System.currentTimeMillis()); return result; } /** * 失败响应 */ public static T ResultT error(Integer code, String msg) { ResultT result new Result(); result.setCode(code); result.setMsg(msg); result.setData(null); result.setTimestamp(System.currentTimeMillis()); return result; } }4.2 自定义业务异常/** * 自定义业务异常 * 用于区分业务主动抛出异常与系统未知异常 */ public class BusinessException extends RuntimeException { private final Integer code; public BusinessException(Integer code, String message) { super(message); this.code code; } public Integer getCode() { return code; } }4.3 全局统一异常处理器核心import lombok.extern.slf4j.Slf4j; import org.springframework.validation.FieldError; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; import java.util.stream.Collectors; /** * 全局统一异常处理器 * 拦截所有Controller层抛出的异常统一封装、统一日志、统一返回 */ Slf4j RestControllerAdvice public class GlobalExceptionHandler { /** * 1. 自定义业务异常最高优先级精准匹配业务报错 */ ExceptionHandler(BusinessException.class) public ResultVoid handleBusinessException(BusinessException e, HttpServletRequest request) { log.error(【业务异常】请求地址{}异常信息{}, request.getRequestURI(), e.getMessage()); return Result.error(e.getCode(), e.getMessage()); } /** * 2. JSR303参数校验异常 */ ExceptionHandler(MethodArgumentNotValidException.class) public ResultVoid handleValidException(MethodArgumentNotValidException e, HttpServletRequest request) { // 拼接所有校验失败信息 String errorMsg e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(,)); log.error(【参数校验异常】请求地址{}异常信息{}, request.getRequestURI(), errorMsg); return Result.error(400, 参数校验失败 errorMsg); } /** * 3. 请求方式不支持异常 */ ExceptionHandler(HttpRequestMethodNotSupportedException.class) public ResultVoid handleMethodException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) { log.error(【请求方式异常】请求地址{}异常信息{}, request.getRequestURI(), e.getMessage()); return Result.error(405, 当前请求方式不支持); } /** * 4. 全局兜底异常最低优先级捕获所有未知异常 * 生产环境屏蔽堆栈信息避免安全泄露 */ ExceptionHandler(Exception.class) public ResultVoid handleAllException(Exception e, HttpServletRequest request) { log.error(【系统未知异常】请求地址{}异常堆栈, request.getRequestURI(), e); return Result.error(500, 系统繁忙请稍后重试); } }4.4 测试接口代码import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; RestController RequestMapping(/api/exception) public class ExceptionTestController { // 测试业务异常 GetMapping(/business) public ResultVoid testBusiness() { throw new BusinessException(4001, 用户权限不足禁止访问); } // 测试系统未知异常 GetMapping(/system) public ResultVoid testSystem() { int num 1 / 0; return Result.success(null); } }五、自定义业务异常适配前文默认使用简易业务异常实现基础报错能力在复杂生产、微服务、多业务模块场景下原生简易异常无法区分业务场景、无法精细化匹配处理、错误码不统一。本节专门扩展企业级自定义异常体系包含自定义异常层级设计、专属异常匹配流程、枚举统一错误码、精细化异常处理器适配实现业务异常标准化、可扩展、可精准匹配、可全局管控。5.1 自定义异常核心设计原理SpringMVC 全局异常处理器支持自定义异常精准优先级匹配机制开发者可基于业务分层自定义异常父类、子类结合错误码枚举统一规范报错类型处理器根据异常继承层级、自定义异常类型完成精准匹配粒度远细于原生 Exception 兜底捕获。设计核心思想业务场景分层异常、错误码统一收口、异常精准匹配、差异化响应提示彻底解决原生异常杂乱、无法细分业务报错的问题。5.2 自定义异常专属匹配执行流程该流程区别于默认异常流程是自定义异常体系的专属流转链路业务层根据场景主动抛出自定义子类业务异常权限/参数/账号/业务熔断等DispatcherServlet 捕获异常进入异常解析器链优先匹配ExceptionHandler精准自定义异常方法遵循子类异常优先于父类异常匹配规则匹配成功后读取异常内部自定义错误码、提示信息、业务描述封装标准化响应体完成业务提示脱敏、日志分级打印终止后续异常解析返回统一 JSON 结果。5.3 完整核心落地代码5.3.1 全局统一错误码枚举统一规范/** * 全局业务错误码枚举 * 统一项目所有业务异常编码适配前后端、微服务统一解析 */ public enum BusinessCodeEnum { // 通用业务异常 4xxx PARAM_VALID_ERROR(4000, 请求参数校验失败), AUTH_ERROR(4001, 用户权限不足禁止访问), REPEAT_SUBMIT(4002, 请勿重复提交请求), TOKEN_EXPIRE(4003, Token已过期请重新登录), // 业务模块异常 5xxx USER_NOT_EXIST(5001, 用户不存在), ORDER_FAIL(5002, 订单创建失败), // 系统异常兜底 SYSTEM_ERROR(9999, 系统繁忙请稍后重试); private final Integer code; private final String msg; BusinessCodeEnum(Integer code, String msg) { this.code code; this.msg msg; } public Integer getCode() { return code; } public String getMsg() { return msg; } }5.3.2 顶层自定义业务父异常/** * 全局顶层业务异常父类 * 所有自定义业务异常统一继承此类方便统一匹配与扩展 */ public class BaseBusinessException extends RuntimeException { // 自定义业务错误码 private final Integer code; // 自定义提示信息 private final String message; public BaseBusinessException(BusinessCodeEnum codeEnum) { super(codeEnum.getMsg()); this.code codeEnum.getCode(); this.message codeEnum.getMsg(); } public BaseBusinessException(BusinessCodeEnum codeEnum, String customMsg) { super(customMsg); this.code codeEnum.getCode(); this.message customMsg; } public Integer getCode() { return code; } Override public String getMessage() { return message; } }5.3.3 细分业务子类异常按需扩展/** * 权限业务异常 */ public class AuthBusinessException extends BaseBusinessException { public AuthBusinessException() { super(BusinessCodeEnum.AUTH_ERROR); } public AuthBusinessException(String customMsg) { super(BusinessCodeEnum.AUTH_ERROR, customMsg); } } /** * 参数校验业务异常 */ public class ParamBusinessException extends BaseBusinessException { public ParamBusinessException() { super(BusinessCodeEnum.PARAM_VALID_ERROR); } public ParamBusinessException(String customMsg) { super(BusinessCodeEnum.PARAM_VALID_ERROR, customMsg); } }5.3.4 适配自定义异常的增强全局异常处理器基于原有处理器改造新增分层自定义异常精准匹配优先级子类细分异常 顶层父异常 参数异常 系统异常import lombok.extern.slf4j.Slf4j; import org.springframework.validation.FieldError; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; import java.util.stream.Collectors; Slf4j RestControllerAdvice public class GlobalExceptionHandler { // 自定义分层业务异常最高优先级 /** * 权限异常精准匹配 */ ExceptionHandler(AuthBusinessException.class) public ResultVoid handleAuthException(AuthBusinessException e, HttpServletRequest request) { log.warn(【权限异常】路径{}信息{}, request.getRequestURI(), e.getMessage()); return Result.error(e.getCode(), e.getMessage()); } /** * 参数业务异常精准匹配 */ ExceptionHandler(ParamBusinessException.class) public ResultVoid handleParamBusinessException(ParamBusinessException e, HttpServletRequest request) { log.warn(【业务参数异常】路径{}信息{}, request.getRequestURI(), e.getMessage()); return Result.error(e.getCode(), e.getMessage()); } /** * 通用顶层业务异常兜底 */ ExceptionHandler(BaseBusinessException.class) public ResultVoid handleBaseBusinessException(BaseBusinessException e, HttpServletRequest request) { log.warn(【通用业务异常】路径{}信息{}, request.getRequestURI(), e.getMessage()); return Result.error(e.getCode(), e.getMessage()); } // 原生框架异常 ExceptionHandler(MethodArgumentNotValidException.class) public ResultVoid handleValidException(MethodArgumentNotValidException e, HttpServletRequest request) { String errorMsg e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(,)); log.error(【参数校验异常】路径{}信息{}, request.getRequestURI(), errorMsg); return Result.error(400, 参数校验失败 errorMsg); } ExceptionHandler(HttpRequestMethodNotSupportedException.class) public ResultVoid handleMethodException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) { log.warn(【请求方式异常】路径{}, request.getRequestURI()); return Result.error(405, 当前请求方式不支持); } // 系统全局兜底 ExceptionHandler(Exception.class) public ResultVoid handleAllException(Exception e, HttpServletRequest request) { log.error(【系统未知异常】路径{}堆栈, request.getRequestURI(), e); return Result.error(9999, 系统繁忙请稍后重试); } }5.3.5 业务使用 测试接口代码RestController RequestMapping(/api/exception) public class ExceptionTestController { // 测试细分权限异常 GetMapping(/auth) public ResultVoid testAuthError() { throw new AuthBusinessException(当前账号无操作权限请联系管理员); } // 测试细分参数异常 GetMapping(/param) public ResultVoid testParamError() { throw new ParamBusinessException(手机号格式非法请检查); } }5.4 自定义异常适配适用场景中大型企业项目需要统一错误码规范、分层管理业务报错方便前后端联调、问题定位微服务分布式项目多服务统一异常枚举避免各服务错误码混乱、响应格式不一致对外开放平台标准化错误码、细分异常类型方便第三方对接、异常归类统计需要精细化告警运维场景不同业务异常分类打印日志、可对接告警平台实现差异化告警。5.5 自定义异常适配优缺点分析5.5.1 优点异常粒度精细化区分权限、参数、订单、用户等不同业务异常精准匹配、差异化处理错误码全局统一通过枚举收口所有业务报错杜绝随机编码、提示混乱问题可扩展性极强新增业务场景仅需新增子类异常、补充枚举无需修改核心处理器运维友好分层日志打印业务异常warn级别、系统异常error级别日志干净易排查适配微服务统一异常体系可无缝适配Feign远程调用、网关统一解析。5.5.2 缺点初期开发成本略高需要维护异常枚举、父类、子类体系小型简单项目略显冗余规范要求严格业务代码必须统一抛出自定义异常若混用原生异常会破坏统一体系子类过多管理成本上升超大项目需按模块分包管理异常类否则类文件泛滥。5.6 自定义异常匹配优先级终版总结细分子类自定义异常 顶层通用业务异常 框架原生异常 系统全局兜底异常六、ErrorController 定制化兜底改造解决404/405原生页面兜底漏洞前文所有RestControllerAdvice全局异常处理器仅能拦截 Controller 执行过程中抛出的异常无法拦截 SpringMVC 原生 WEB 基础异常404路径不存在、405请求方式不支持、400参数解析失败等。这类请求不会进入 Controller 方法导致全局异常处理器失效、返回原生白色错误页面/原生JSON造成前后端格式不统一、生产报错错乱问题。SpringBoot 提供ErrorController全局兜底错误控制器是 Web 层级最后一道异常兜底屏障本节完成企业级定制化改造实现「RestControllerAdvice业务异常拦截 ErrorController 原生WEB异常兜底」双闭环全覆盖。6.1 原生默认机制缺陷必须改造的原因拦截范围断层404、405、400、500容器级异常不进入Controller全局异常注解处理器无法捕获返回格式不统一浏览器访问返回默认WhiteLabel白页接口访问返回原生错误JSON与项目统一Result格式冲突存在安全隐患默认错误响应会携带服务器路径、请求参数、堆栈概要泄露项目信息无法统一运维原生异常无统一日志、无统一错误码、无法对接告警平台。6.2 ErrorController 底层实现原理根据 SpringBoot 官方机制所有未被 Controller 捕获、未被异常解析器处理的 WEB 错误最终都会转发到/error请求路径由默认BasicErrorController统一处理。开发者可通过自定义实现 ErrorController 接口 覆盖 /error 端点替换系统默认兜底控制器实现全网所有请求异常统一格式化返回。双兜底最终优先级自定义ControllerAdvice精准异常拦截 自定义ErrorController全局WEB兜底 Spring默认错误处理器6.3 定制化改造完整执行流程客户端发起非法请求不存在路径/错误请求方式DispatcherServlet 分发请求匹配不到有效 Handler 或请求预处理失败不会进入自定义全局异常处理器RestControllerAdvice失效Spring 自动转发至内部/error端点自定义 ErrorController 接管错误请求获取错误状态码、请求信息统一封装为项目 Result 标准响应、分级打印日志、脱敏原生错误信息返回标准JSON完成全网异常统一兜底。6.4 ErrorController 定制化核心代码6.4.1 自定义全局兜底错误控制器import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; /** * 全局兜底错误控制器定制化改造 * 专门兜底 404/405/400 等所有无法进入Controller的WEB异常 * 补足 RestControllerAdvice 拦截盲区实现全网异常统一格式 */ Slf4j RestController public class GlobalErrorController implements ErrorController { private static final String ERROR_PATH /error; /** * 接管所有WEB底层错误 */ RequestMapping(ERROR_PATH) public ResultVoid handleError(HttpServletRequest request) { // 获取原生HTTP状态码 Integer status (Integer) request.getAttribute(javax.servlet.error.status_code); // 根据状态码自定义标准化提示 if (HttpStatus.NOT_FOUND.value() status) { log.warn(【WEB兜底异常】404路径不存在请求地址{}, request.getRequestURI()); return Result.error(404, 请求资源不存在); } if (HttpStatus.METHOD_NOT_ALLOWED.value() status) { log.warn(【WEB兜底异常】405请求方式不支持请求地址{}, request.getRequestURI()); return Result.error(405, 当前请求方式不被支持); } if (HttpStatus.BAD_REQUEST.value() status) { log.warn(【WEB兜底异常】400参数错误请求地址{}, request.getRequestURI()); return Result.error(400, 请求参数非法); } // 全局未知WEB异常兜底 log.error(【WEB系统异常】服务器未知错误状态码{}请求地址{}, status, request.getRequestURI()); return Result.error(500, 服务器繁忙请稍后重试); } /** * 重写错误路径覆盖默认控制器 */ Override public String getErrorPath() { return ERROR_PATH; } }6.5 双兜底联动完整架构说明改造完成后项目形成双层异常防护闭环彻底消除异常拦截盲区第一层业务层拦截RestControllerAdvice拦截进入Controller后的业务异常、参数校验异常、代码运行异常特点精准匹配、分级处理、业务精细化提示。第二层容器层兜底自定义ErrorController拦截404、405、400、请求预处理失败、无匹配处理器等所有WEB底层错误特点拦截所有盲区请求全网统一响应格式。6.6 ErrorController 改造适用场景前后端分离项目必备杜绝前端收到白页、原生错误JSON全网接口格式统一对外开放API平台非法路径、恶意爬虫扫描统一友好返回保护服务信息微服务网关下游服务避免无效请求报错泛滥统一日志与错误码生产严格规范项目消除所有异常拦截盲区满足运维监控、日志统计规范。6.7 定制化改造优缺点分析6.7.1 优点彻底消除拦截盲区补足全局异常处理器短板实现100%请求异常全覆盖全网响应格式统一无论是业务报错还是容器级报错结构完全一致安全性提升屏蔽原生错误堆栈、服务器信息脱敏对外错误提示运维统一可控所有异常统一日志、统一错误码可正常接入告警与链路追踪。6.7.2 缺点与避坑点需要手动维护状态码新增特殊HTTP状态码需要手动补充判断逻辑避免重复拦截正常Controller异常已被Advice拦截不会进入ErrorController无需担心重复处理禁止自定义跳转页面前后端分离项目必须返回JSON不能返回视图否则适配前端失败。6.8 全网最终异常优先级完整版细分自定义业务异常 通用业务异常 框架参数异常 系统兜底异常 自定义ErrorController容器级兜底 Spring原生默认错误机制
SpringMVC 全局统一异常处理
发布时间:2026/7/13 14:07:37
前置关联说明在《SpringMVC定制化配置全解》中我们完成了视图适配、拦截器、跨域、JSON序列化、国际化等请求链路定制覆盖了请求入参、拦截、响应格式化场景。本文将独立深度拆解SpringMVC 全局统一异常处理补齐MVC请求闭环最后一环。一、全局统一异常处理的核心意义SpringMVC 原生异常机制存在明显的生产缺陷未做统一处理时项目会出现接口返回杂乱、代码冗余、安全泄露、运维困难等问题也是企业开发强制统一规范的核心原因。原生缺陷汇总响应格式不统一业务异常、代码异常、参数异常返回结构完全不同前端需要多套逻辑适配维护成本极高。业务代码严重冗余每个 Controller、Service 都需要手动 try-catch异常处理逻辑与业务逻辑高度耦合。存在系统安全泄露风险默认异常会直接返回堆栈信息、类路径、服务器报错详情暴露项目结构与源码信息。异常无法统一管控异常散乱抛出无法实现统一日志打印、统一告警、统一降级提示。用户体验差原生 500、404、400 错误无友好中文提示无法适配业务场景。统一异常处理核心价值实现异常解耦、格式统一、安全脱敏、日志可追溯、运维可监控是前后端分离、微服务、中台项目的基础核心基建。二、SpringMVC 异常处理完整执行流程本节细化完整请求异常流转链路从请求接入到异常捕获、解析、封装、响应返回全流程拆解是理解底层原理的核心。2.1 整体执行链路客户端发起请求 → Tomcat 容器接收请求 → DispatcherServlet 前端控制器分发请求 → 拦截器 preHandle 预处理 → Controller 处理器执行 → Service 业务逻辑执行 →代码抛出异常→ DispatcherServlet 捕获全局异常 → 委派 HandlerExceptionResolver 解析器链处理 → 匹配自定义全局异常处理器 → 异常信息脱敏、封装标准化结果 → 输出 JSON 响应给前端 → 拦截器 afterCompletion 回调收尾 → 请求结束2.2 异常匹配核心流程规则异常抛出后不会直接返回由DispatcherServlet 统一拦截终止异常向外冒泡遍历 Spring 内置异常解析器链按照优先级从高到低匹配对应异常处理器精准异常处理器优先匹配业务异常 参数异常 请求异常 通用兜底异常匹配成功后直接执行对应处理逻辑终止后续解析器遍历所有异常统一封装为自定义响应体屏蔽原生异常信息实现安全脱敏。三、底层实现原理Spring 官方规范3.1 核心核心组件SpringMVC 所有异常处理均基于HandlerExceptionResolver异常解析器接口实现框架默认内置三级解析器优先级由高到低ExceptionHandlerExceptionResolver最高优先级专门处理ExceptionHandler注解方法是全局异常处理的核心底层支撑。ResponseStatusExceptionResolver处理标记了ResponseStatus注解的自定义状态码异常。DefaultHandlerExceptionResolver兜底最低优先级框架默认兜底处理 404、405、400 等原生 Web 异常。3.2 全局异常注解原理RestControllerAdvice全局控制器增强注解SpringMVC 提供的全局切面能力仅作用于所有 RestController不依赖 AOP 动态代理不侵入业务代码。工作机制项目启动时自动扫描所有被ExceptionHandler标记的方法构建异常匹配映射表请求异常时自动根据异常类型精准匹配对应处理方法。3.3 优先级终版规则自定义精准异常处理方法 通用兜底异常方法 Spring 内置默认异常解析器四、生产级核心实战代码整套方案包含统一响应实体、自定义业务异常、全局异常处理器、多类型异常捕获、日志记录、安全脱敏、测试接口零改造可直接用于生产环境。4.1 全局统一响应实体import lombok.Data; /** * 全局统一响应实体 * 统一成功/异常返回格式前后端通用约定 */ Data public class ResultT { // 响应状态码200成功4xx客户端异常5xx服务端异常 private Integer code; // 响应提示信息 private String msg; // 响应业务数据 private T data; // 响应时间戳 private long timestamp; /** * 成功响应 */ public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMsg(操作成功); result.setData(data); result.setTimestamp(System.currentTimeMillis()); return result; } /** * 失败响应 */ public static T ResultT error(Integer code, String msg) { ResultT result new Result(); result.setCode(code); result.setMsg(msg); result.setData(null); result.setTimestamp(System.currentTimeMillis()); return result; } }4.2 自定义业务异常/** * 自定义业务异常 * 用于区分业务主动抛出异常与系统未知异常 */ public class BusinessException extends RuntimeException { private final Integer code; public BusinessException(Integer code, String message) { super(message); this.code code; } public Integer getCode() { return code; } }4.3 全局统一异常处理器核心import lombok.extern.slf4j.Slf4j; import org.springframework.validation.FieldError; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; import java.util.stream.Collectors; /** * 全局统一异常处理器 * 拦截所有Controller层抛出的异常统一封装、统一日志、统一返回 */ Slf4j RestControllerAdvice public class GlobalExceptionHandler { /** * 1. 自定义业务异常最高优先级精准匹配业务报错 */ ExceptionHandler(BusinessException.class) public ResultVoid handleBusinessException(BusinessException e, HttpServletRequest request) { log.error(【业务异常】请求地址{}异常信息{}, request.getRequestURI(), e.getMessage()); return Result.error(e.getCode(), e.getMessage()); } /** * 2. JSR303参数校验异常 */ ExceptionHandler(MethodArgumentNotValidException.class) public ResultVoid handleValidException(MethodArgumentNotValidException e, HttpServletRequest request) { // 拼接所有校验失败信息 String errorMsg e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(,)); log.error(【参数校验异常】请求地址{}异常信息{}, request.getRequestURI(), errorMsg); return Result.error(400, 参数校验失败 errorMsg); } /** * 3. 请求方式不支持异常 */ ExceptionHandler(HttpRequestMethodNotSupportedException.class) public ResultVoid handleMethodException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) { log.error(【请求方式异常】请求地址{}异常信息{}, request.getRequestURI(), e.getMessage()); return Result.error(405, 当前请求方式不支持); } /** * 4. 全局兜底异常最低优先级捕获所有未知异常 * 生产环境屏蔽堆栈信息避免安全泄露 */ ExceptionHandler(Exception.class) public ResultVoid handleAllException(Exception e, HttpServletRequest request) { log.error(【系统未知异常】请求地址{}异常堆栈, request.getRequestURI(), e); return Result.error(500, 系统繁忙请稍后重试); } }4.4 测试接口代码import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; RestController RequestMapping(/api/exception) public class ExceptionTestController { // 测试业务异常 GetMapping(/business) public ResultVoid testBusiness() { throw new BusinessException(4001, 用户权限不足禁止访问); } // 测试系统未知异常 GetMapping(/system) public ResultVoid testSystem() { int num 1 / 0; return Result.success(null); } }五、自定义业务异常适配前文默认使用简易业务异常实现基础报错能力在复杂生产、微服务、多业务模块场景下原生简易异常无法区分业务场景、无法精细化匹配处理、错误码不统一。本节专门扩展企业级自定义异常体系包含自定义异常层级设计、专属异常匹配流程、枚举统一错误码、精细化异常处理器适配实现业务异常标准化、可扩展、可精准匹配、可全局管控。5.1 自定义异常核心设计原理SpringMVC 全局异常处理器支持自定义异常精准优先级匹配机制开发者可基于业务分层自定义异常父类、子类结合错误码枚举统一规范报错类型处理器根据异常继承层级、自定义异常类型完成精准匹配粒度远细于原生 Exception 兜底捕获。设计核心思想业务场景分层异常、错误码统一收口、异常精准匹配、差异化响应提示彻底解决原生异常杂乱、无法细分业务报错的问题。5.2 自定义异常专属匹配执行流程该流程区别于默认异常流程是自定义异常体系的专属流转链路业务层根据场景主动抛出自定义子类业务异常权限/参数/账号/业务熔断等DispatcherServlet 捕获异常进入异常解析器链优先匹配ExceptionHandler精准自定义异常方法遵循子类异常优先于父类异常匹配规则匹配成功后读取异常内部自定义错误码、提示信息、业务描述封装标准化响应体完成业务提示脱敏、日志分级打印终止后续异常解析返回统一 JSON 结果。5.3 完整核心落地代码5.3.1 全局统一错误码枚举统一规范/** * 全局业务错误码枚举 * 统一项目所有业务异常编码适配前后端、微服务统一解析 */ public enum BusinessCodeEnum { // 通用业务异常 4xxx PARAM_VALID_ERROR(4000, 请求参数校验失败), AUTH_ERROR(4001, 用户权限不足禁止访问), REPEAT_SUBMIT(4002, 请勿重复提交请求), TOKEN_EXPIRE(4003, Token已过期请重新登录), // 业务模块异常 5xxx USER_NOT_EXIST(5001, 用户不存在), ORDER_FAIL(5002, 订单创建失败), // 系统异常兜底 SYSTEM_ERROR(9999, 系统繁忙请稍后重试); private final Integer code; private final String msg; BusinessCodeEnum(Integer code, String msg) { this.code code; this.msg msg; } public Integer getCode() { return code; } public String getMsg() { return msg; } }5.3.2 顶层自定义业务父异常/** * 全局顶层业务异常父类 * 所有自定义业务异常统一继承此类方便统一匹配与扩展 */ public class BaseBusinessException extends RuntimeException { // 自定义业务错误码 private final Integer code; // 自定义提示信息 private final String message; public BaseBusinessException(BusinessCodeEnum codeEnum) { super(codeEnum.getMsg()); this.code codeEnum.getCode(); this.message codeEnum.getMsg(); } public BaseBusinessException(BusinessCodeEnum codeEnum, String customMsg) { super(customMsg); this.code codeEnum.getCode(); this.message customMsg; } public Integer getCode() { return code; } Override public String getMessage() { return message; } }5.3.3 细分业务子类异常按需扩展/** * 权限业务异常 */ public class AuthBusinessException extends BaseBusinessException { public AuthBusinessException() { super(BusinessCodeEnum.AUTH_ERROR); } public AuthBusinessException(String customMsg) { super(BusinessCodeEnum.AUTH_ERROR, customMsg); } } /** * 参数校验业务异常 */ public class ParamBusinessException extends BaseBusinessException { public ParamBusinessException() { super(BusinessCodeEnum.PARAM_VALID_ERROR); } public ParamBusinessException(String customMsg) { super(BusinessCodeEnum.PARAM_VALID_ERROR, customMsg); } }5.3.4 适配自定义异常的增强全局异常处理器基于原有处理器改造新增分层自定义异常精准匹配优先级子类细分异常 顶层父异常 参数异常 系统异常import lombok.extern.slf4j.Slf4j; import org.springframework.validation.FieldError; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; import java.util.stream.Collectors; Slf4j RestControllerAdvice public class GlobalExceptionHandler { // 自定义分层业务异常最高优先级 /** * 权限异常精准匹配 */ ExceptionHandler(AuthBusinessException.class) public ResultVoid handleAuthException(AuthBusinessException e, HttpServletRequest request) { log.warn(【权限异常】路径{}信息{}, request.getRequestURI(), e.getMessage()); return Result.error(e.getCode(), e.getMessage()); } /** * 参数业务异常精准匹配 */ ExceptionHandler(ParamBusinessException.class) public ResultVoid handleParamBusinessException(ParamBusinessException e, HttpServletRequest request) { log.warn(【业务参数异常】路径{}信息{}, request.getRequestURI(), e.getMessage()); return Result.error(e.getCode(), e.getMessage()); } /** * 通用顶层业务异常兜底 */ ExceptionHandler(BaseBusinessException.class) public ResultVoid handleBaseBusinessException(BaseBusinessException e, HttpServletRequest request) { log.warn(【通用业务异常】路径{}信息{}, request.getRequestURI(), e.getMessage()); return Result.error(e.getCode(), e.getMessage()); } // 原生框架异常 ExceptionHandler(MethodArgumentNotValidException.class) public ResultVoid handleValidException(MethodArgumentNotValidException e, HttpServletRequest request) { String errorMsg e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(,)); log.error(【参数校验异常】路径{}信息{}, request.getRequestURI(), errorMsg); return Result.error(400, 参数校验失败 errorMsg); } ExceptionHandler(HttpRequestMethodNotSupportedException.class) public ResultVoid handleMethodException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) { log.warn(【请求方式异常】路径{}, request.getRequestURI()); return Result.error(405, 当前请求方式不支持); } // 系统全局兜底 ExceptionHandler(Exception.class) public ResultVoid handleAllException(Exception e, HttpServletRequest request) { log.error(【系统未知异常】路径{}堆栈, request.getRequestURI(), e); return Result.error(9999, 系统繁忙请稍后重试); } }5.3.5 业务使用 测试接口代码RestController RequestMapping(/api/exception) public class ExceptionTestController { // 测试细分权限异常 GetMapping(/auth) public ResultVoid testAuthError() { throw new AuthBusinessException(当前账号无操作权限请联系管理员); } // 测试细分参数异常 GetMapping(/param) public ResultVoid testParamError() { throw new ParamBusinessException(手机号格式非法请检查); } }5.4 自定义异常适配适用场景中大型企业项目需要统一错误码规范、分层管理业务报错方便前后端联调、问题定位微服务分布式项目多服务统一异常枚举避免各服务错误码混乱、响应格式不一致对外开放平台标准化错误码、细分异常类型方便第三方对接、异常归类统计需要精细化告警运维场景不同业务异常分类打印日志、可对接告警平台实现差异化告警。5.5 自定义异常适配优缺点分析5.5.1 优点异常粒度精细化区分权限、参数、订单、用户等不同业务异常精准匹配、差异化处理错误码全局统一通过枚举收口所有业务报错杜绝随机编码、提示混乱问题可扩展性极强新增业务场景仅需新增子类异常、补充枚举无需修改核心处理器运维友好分层日志打印业务异常warn级别、系统异常error级别日志干净易排查适配微服务统一异常体系可无缝适配Feign远程调用、网关统一解析。5.5.2 缺点初期开发成本略高需要维护异常枚举、父类、子类体系小型简单项目略显冗余规范要求严格业务代码必须统一抛出自定义异常若混用原生异常会破坏统一体系子类过多管理成本上升超大项目需按模块分包管理异常类否则类文件泛滥。5.6 自定义异常匹配优先级终版总结细分子类自定义异常 顶层通用业务异常 框架原生异常 系统全局兜底异常六、ErrorController 定制化兜底改造解决404/405原生页面兜底漏洞前文所有RestControllerAdvice全局异常处理器仅能拦截 Controller 执行过程中抛出的异常无法拦截 SpringMVC 原生 WEB 基础异常404路径不存在、405请求方式不支持、400参数解析失败等。这类请求不会进入 Controller 方法导致全局异常处理器失效、返回原生白色错误页面/原生JSON造成前后端格式不统一、生产报错错乱问题。SpringBoot 提供ErrorController全局兜底错误控制器是 Web 层级最后一道异常兜底屏障本节完成企业级定制化改造实现「RestControllerAdvice业务异常拦截 ErrorController 原生WEB异常兜底」双闭环全覆盖。6.1 原生默认机制缺陷必须改造的原因拦截范围断层404、405、400、500容器级异常不进入Controller全局异常注解处理器无法捕获返回格式不统一浏览器访问返回默认WhiteLabel白页接口访问返回原生错误JSON与项目统一Result格式冲突存在安全隐患默认错误响应会携带服务器路径、请求参数、堆栈概要泄露项目信息无法统一运维原生异常无统一日志、无统一错误码、无法对接告警平台。6.2 ErrorController 底层实现原理根据 SpringBoot 官方机制所有未被 Controller 捕获、未被异常解析器处理的 WEB 错误最终都会转发到/error请求路径由默认BasicErrorController统一处理。开发者可通过自定义实现 ErrorController 接口 覆盖 /error 端点替换系统默认兜底控制器实现全网所有请求异常统一格式化返回。双兜底最终优先级自定义ControllerAdvice精准异常拦截 自定义ErrorController全局WEB兜底 Spring默认错误处理器6.3 定制化改造完整执行流程客户端发起非法请求不存在路径/错误请求方式DispatcherServlet 分发请求匹配不到有效 Handler 或请求预处理失败不会进入自定义全局异常处理器RestControllerAdvice失效Spring 自动转发至内部/error端点自定义 ErrorController 接管错误请求获取错误状态码、请求信息统一封装为项目 Result 标准响应、分级打印日志、脱敏原生错误信息返回标准JSON完成全网异常统一兜底。6.4 ErrorController 定制化核心代码6.4.1 自定义全局兜底错误控制器import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; /** * 全局兜底错误控制器定制化改造 * 专门兜底 404/405/400 等所有无法进入Controller的WEB异常 * 补足 RestControllerAdvice 拦截盲区实现全网异常统一格式 */ Slf4j RestController public class GlobalErrorController implements ErrorController { private static final String ERROR_PATH /error; /** * 接管所有WEB底层错误 */ RequestMapping(ERROR_PATH) public ResultVoid handleError(HttpServletRequest request) { // 获取原生HTTP状态码 Integer status (Integer) request.getAttribute(javax.servlet.error.status_code); // 根据状态码自定义标准化提示 if (HttpStatus.NOT_FOUND.value() status) { log.warn(【WEB兜底异常】404路径不存在请求地址{}, request.getRequestURI()); return Result.error(404, 请求资源不存在); } if (HttpStatus.METHOD_NOT_ALLOWED.value() status) { log.warn(【WEB兜底异常】405请求方式不支持请求地址{}, request.getRequestURI()); return Result.error(405, 当前请求方式不被支持); } if (HttpStatus.BAD_REQUEST.value() status) { log.warn(【WEB兜底异常】400参数错误请求地址{}, request.getRequestURI()); return Result.error(400, 请求参数非法); } // 全局未知WEB异常兜底 log.error(【WEB系统异常】服务器未知错误状态码{}请求地址{}, status, request.getRequestURI()); return Result.error(500, 服务器繁忙请稍后重试); } /** * 重写错误路径覆盖默认控制器 */ Override public String getErrorPath() { return ERROR_PATH; } }6.5 双兜底联动完整架构说明改造完成后项目形成双层异常防护闭环彻底消除异常拦截盲区第一层业务层拦截RestControllerAdvice拦截进入Controller后的业务异常、参数校验异常、代码运行异常特点精准匹配、分级处理、业务精细化提示。第二层容器层兜底自定义ErrorController拦截404、405、400、请求预处理失败、无匹配处理器等所有WEB底层错误特点拦截所有盲区请求全网统一响应格式。6.6 ErrorController 改造适用场景前后端分离项目必备杜绝前端收到白页、原生错误JSON全网接口格式统一对外开放API平台非法路径、恶意爬虫扫描统一友好返回保护服务信息微服务网关下游服务避免无效请求报错泛滥统一日志与错误码生产严格规范项目消除所有异常拦截盲区满足运维监控、日志统计规范。6.7 定制化改造优缺点分析6.7.1 优点彻底消除拦截盲区补足全局异常处理器短板实现100%请求异常全覆盖全网响应格式统一无论是业务报错还是容器级报错结构完全一致安全性提升屏蔽原生错误堆栈、服务器信息脱敏对外错误提示运维统一可控所有异常统一日志、统一错误码可正常接入告警与链路追踪。6.7.2 缺点与避坑点需要手动维护状态码新增特殊HTTP状态码需要手动补充判断逻辑避免重复拦截正常Controller异常已被Advice拦截不会进入ErrorController无需担心重复处理禁止自定义跳转页面前后端分离项目必须返回JSON不能返回视图否则适配前端失败。6.8 全网最终异常优先级完整版细分自定义业务异常 通用业务异常 框架参数异常 系统兜底异常 自定义ErrorController容器级兜底 Spring原生默认错误机制