统一异常处理 GlobalExceptionHandler

article/2025/11/9 5:36:37

平时经常在代码里面

try {...
} catch (Exception e) {...
}

这样子代码繁琐而且也不好看。特别是还有一些需要自己new一个map封装业务的异常信息,是在是难看。
所以就需要引出全局异常。经过一些封装,就可以实现这样的效果了。
image.png

还是看代码吧!
首先是定义了一个GlobalExceptionHandler组件,通过添加@RestControllerAdvice注解,作为默认的Controller全局异常处理增强组件,在这个组件中分别对 系统级别未知系统、客户端异常、服务端异常 都分别做了统一处理。

/*** 默认的Controller全局异常处理增强组件**/
@Slf4j
@RestControllerAdvice
@Order
public class GlobalExceptionHandler {// =========== 系统级别未知异常 =========@ExceptionHandler(value = Exception.class)public JsonResult<Object> handle(Exception e) {log.error("[ 系统未知错误 ]", e);return JsonResult.buildError(CommonErrorCodeEnum.SYSTEM_UNKNOWN_ERROR);}// =========== 客户端异常 =========/*** 1001 HTTP请求方法类型错误** @param e* @return*/@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)public JsonResult<Object> handle(HttpRequestMethodNotSupportedException e) {log.error("[客户端HTTP请求方法错误]", e);return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_HTTP_METHOD_ERROR);}/*** 1002 客户端请求体参数校验不通过** @param e* @return*/@ExceptionHandler(value = MethodArgumentNotValidException.class)public JsonResult<Object> handle(MethodArgumentNotValidException e) {log.error("[客户端请求体参数校验不通过]", e);String errorMsg = this.handle(e.getBindingResult().getFieldErrors());return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_BODY_CHECK_ERROR.getErrorCode(), errorMsg);}private String handle(List<FieldError> fieldErrors) {StringBuilder sb = new StringBuilder();for (FieldError obj : fieldErrors) {sb.append(obj.getField());sb.append("=[");sb.append(obj.getDefaultMessage());sb.append("]  ");}return sb.toString();}/*** 1003 客户端请求体JSON格式错误或字段类型不匹配** @param e* @return*/@ExceptionHandler(value = HttpMessageNotReadableException.class)public JsonResult<Object> handle(HttpMessageNotReadableException e) {log.error("[客户端请求体JSON格式错误或字段类型不匹配]", e);return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_BODY_FORMAT_ERROR);}/*** 1004 客户端URL中的参数类型错误** @param e* @return*/@ExceptionHandler(value = BindException.class)public JsonResult<Object> handle(BindException e) {log.error("[客户端URL中的参数类型错误]", e);FieldError fieldError = e.getBindingResult().getFieldError();String errorMsg = null;if (fieldError != null) {errorMsg = fieldError.getDefaultMessage();if (errorMsg != null && errorMsg.contains("java.lang.NumberFormatException")) {errorMsg = fieldError.getField() + "参数类型错误";}}if (errorMsg != null && !"".equals(errorMsg)) {return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_PATH_VARIABLE_ERROR.getErrorCode(), errorMsg);}return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_PATH_VARIABLE_ERROR);}/*** 1005 客户端请求参数校验不通过** @param e* @return*/@ExceptionHandler(value = ConstraintViolationException.class)public JsonResult<Object> handle(ConstraintViolationException e) {log.error("[客户端请求参数校验不通过]", e);Iterator<ConstraintViolation<?>> it = e.getConstraintViolations().iterator();String errorMsg = null;if (it.hasNext()) {errorMsg = it.next().getMessageTemplate();}if (errorMsg != null && !"".equals(errorMsg)) {return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_CHECK_ERROR.getErrorCode(), errorMsg);}return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_CHECK_ERROR);}/*** 1006 客户端请求缺少必填的参数** @param e* @return*/@ExceptionHandler(value = MissingServletRequestParameterException.class)public JsonResult<Object> handle(MissingServletRequestParameterException e) {log.error("[客户端请求缺少必填的参数]", e);String errorMsg = null;String parameterName = e.getParameterName();if (!"".equals(parameterName)) {errorMsg = parameterName + "不能为空";}if (errorMsg != null) {return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_REQUIRED_ERROR.getErrorCode(), errorMsg);}return JsonResult.buildError(CommonErrorCodeEnum.CLIENT_REQUEST_PARAM_REQUIRED_ERROR);}// =========== 服务端异常 =========/*** 2001 业务方法参数检查不通过** @param e* @return*/@ExceptionHandler(value = IllegalArgumentException.class)public JsonResult<Object> handle(IllegalArgumentException e) {log.error("[业务方法参数检查不通过]", e);return JsonResult.buildError(CommonErrorCodeEnum.SERVER_ILLEGAL_ARGUMENT_ERROR);}/*** 系统自定义业务异常** @param e* @return*/@ExceptionHandler(value = BaseBizException.class)public JsonResult<Object> handle(BaseBizException e) {log.error("[ 业务异常 ]", e);return JsonResult.buildError(e.getErrorCode(), e.getErrorMsg());}

其次是BaseBizException自定义基础业务异常类

/*** 基础自定义业务异常*/
public class BaseBizException extends RuntimeException {/*** 默认错误码*/private static final String DEFAULT_ERROR_CODE = "-1";private String errorCode;private String errorMsg;public BaseBizException(String errorMsg) {super(errorMsg);this.errorCode = DEFAULT_ERROR_CODE;this.errorMsg = errorMsg;}public BaseBizException(String errorCode, String errorMsg) {super(errorMsg);this.errorCode = errorCode;this.errorMsg = errorMsg;}public BaseBizException(BaseErrorCodeEnum baseErrorCodeEnum) {super(baseErrorCodeEnum.getErrorMsg());this.errorCode = baseErrorCodeEnum.getErrorCode();this.errorMsg = baseErrorCodeEnum.getErrorMsg();}public BaseBizException(String errorCode, String errorMsg, Object... arguments) {super(MessageFormat.format(errorMsg, arguments));this.errorCode = errorCode;this.errorMsg = MessageFormat.format(errorMsg, arguments);}public BaseBizException(BaseErrorCodeEnum baseErrorCodeEnum, Object... arguments) {super(MessageFormat.format(baseErrorCodeEnum.getErrorMsg(), arguments));this.errorCode = baseErrorCodeEnum.getErrorCode();this.errorMsg = MessageFormat.format(baseErrorCodeEnum.getErrorMsg(), arguments);}public String getErrorCode() {return errorCode;}public void setErrorCode(String errorCode) {this.errorCode = errorCode;}public String getErrorMsg() {return errorMsg;}public void setErrorMsg(String errorMsg) {this.errorMsg = errorMsg;}
}

异常错误码枚举抽象定义

/*** 异常错误码枚举抽象定义**/
public interface BaseErrorCodeEnum {String getErrorCode();String getErrorMsg();
}

系统通用的业务异常错误码枚举

/*** 系统通用的业务异常错误码枚举* @version 1.0*/
public enum CommonErrorCodeEnum implements BaseErrorCodeEnum {// =========== 系统级别未知异常 =========/*** 系统未知错误*/SYSTEM_UNKNOWN_ERROR("-1", "系统未知错误"),// =========== 客户端异常 =========/*** 客户端HTTP请求方法错误* org.springframework.web.HttpRequestMethodNotSupportedException*/CLIENT_HTTP_METHOD_ERROR("1001", "客户端HTTP请求方法错误"),/*** 客户端request body参数错误* 主要是未能通过Hibernate Validator校验的异常处理* <p>* org.springframework.web.bind.MethodArgumentNotValidException*/CLIENT_REQUEST_BODY_CHECK_ERROR("1002", "客户端请求体参数校验不通过"),/*** 客户端@RequestBody请求体JSON格式错误或字段类型错误* org.springframework.http.converter.HttpMessageNotReadableException* <p>* eg:* 1、参数类型不对:{"test":"abc"},本身类型是Long* 2、{"test":}  test属性没有给值*/CLIENT_REQUEST_BODY_FORMAT_ERROR("1003", "客户端请求体JSON格式错误或字段类型不匹配"),/*** 客户端@PathVariable参数错误* 一般是类型不匹配,比如本来是Long类型,客户端却给了一个无法转换成Long字符串* org.springframework.validation.BindException*/CLIENT_PATH_VARIABLE_ERROR("1004", "客户端URL中的参数类型错误"),/*** 客户端@RequestParam参数校验不通过* 主要是未能通过Hibernate Validator校验的异常处理* javax.validation.ConstraintViolationException*/CLIENT_REQUEST_PARAM_CHECK_ERROR("1005", "客户端请求参数校验不通过"),/*** 客户端@RequestParam参数必填* 入参中的@RequestParam注解设置了必填,但是客户端没有给值* javax.validation.ConstraintViolationException*/CLIENT_REQUEST_PARAM_REQUIRED_ERROR("1006", "客户端请求缺少必填的参数"),// =========== 服务端异常 =========/*** 通用的业务方法入参检查错误* java.lang.IllegalArgumentException*/SERVER_ILLEGAL_ARGUMENT_ERROR("2001", "业务方法参数检查不通过"),;private String errorCode;private String errorMsg;CommonErrorCodeEnum(String errorCode, String errorMsg) {this.errorCode = errorCode;this.errorMsg = errorMsg;}@Overridepublic String getErrorCode() {return errorCode;}public void setErrorCode(String errorCode) {this.errorCode = errorCode;}@Overridepublic String getErrorMsg() {return errorMsg;}public void setErrorMsg(String errorMsg) {this.errorMsg = errorMsg;}
}

在业务代码的开发中,可以直接抛出这个异常类,也可以在具体的业务代码工程新建一个类继承BaseBizException,然后抛出自定义的异常类。比如如在客户模块中,新建一个CustomerBizException自定义类,然后继承BaseBizException,


public class CustomerBizException extends BaseBizException {public CustomerBizException(String errorMsg) {super(errorMsg);}public CustomerBizException(String errorCode, String errorMsg) {super(errorCode, errorMsg);}public CustomerBizException(BaseErrorCodeEnum baseErrorCodeEnum) {super(baseErrorCodeEnum);}public CustomerBizException(String errorCode, String errorMsg, Object... arguments) {super(errorCode, errorMsg, arguments);}public CustomerBizException(BaseErrorCodeEnum baseErrorCodeEnum, Object... arguments) {super(baseErrorCodeEnum, arguments);}
}
/*** 异常错误码枚举值* 前三位代表服务,后三位代表功能错误码*/
public enum CustomerErrorCodeEnum implements BaseErrorCodeEnum {CUSTOMER_AUDIT_REPEAT("200000", "客服审核重复"),;private String errorCode;private String errorMsg;CustomerErrorCodeEnum(String errorCode, String errorMsg) {this.errorCode = errorCode;this.errorMsg = errorMsg;}@Overridepublic String getErrorCode() {return errorCode;}public void setErrorCode(String errorCode) {this.errorCode = errorCode;}@Overridepublic String getErrorMsg() {return errorMsg;}public void setErrorMsg(String errorMsg) {this.errorMsg = errorMsg;}}

最后在业务代码中抛出 CustomerBizException即可。如下:

@RestController
@RequestMapping("/exception")
public class ExceptionController {@GetMapping("/test")public JsonResult<String> exceptionTest(String num) {if (num.equals("22")) {throw new CustomerBizException(CustomerErrorCodeEnum.CUSTOMER_AUDIT_REPEAT);}}

image.png

spring:# 出现错误时, 直接抛出异常(便于异常统一处理,否则捕获不到404)mvc:throw-exception-if-no-handler-found: true# 不要为工程中的资源文件建立映射web:resources:add-mappings: false

如果你是Spring Boot 2.4.0 以下的版本,配置需要这样写

spring:# 出现错误时, 直接抛出异常(便于异常统一处理,否则捕获不到404)mvc:throw-exception-if-no-handler-found: true# 不要为工程中的资源文件建立映射resources:add-mappings: false

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.4.0-Configuration-Changelog

有了这个全局异常处理,就有多种玩法了。可以写一个参数的校验的工具类。比如这样子的,不用自己写if…return;

欢迎评论私信交流!!

也可以看看sprainkle大佬的这两篇文章
https://www.jianshu.com/p/3f3d9e8d1efa
https://www.jianshu.com/p/179daa24ef52


http://chatgpt.dhexx.cn/article/ODKFYfFt.shtml

相关文章

【异常】使用ApiFox客户端提示 “ 加载出错,请检查网络是否异常”,但是Web端是正常访问的

一、报错内容 提示内容类似于如下&#xff1a;连接Apifox服务器出错&#xff0c;请检查网络是否异常 二、报错说明 可能是因为APIFOX升级版本了&#xff0c;导致的问题&#xff0c;目前没有找到根因 三、报错解决 3.1 解决方案 1 使用Chrome的插件——Apifox Agent&#…

(七)客户端异常退出事件处理

如果客户端异常退出了&#xff0c;我们会从服务端记录用户连接的 _connMap 表中找到改用户&#xff0c;如果它断连了就从此表中删除&#xff0c;并设置其状态为 offline。 因为记录用户连接情况的表是可能被多个线程操作的&#xff0c;所以访问表的操作都要使用互斥锁进行保护。…

彻底解决aapt2异常

随时随地阅读更多技术实战干货&#xff0c;获取项目源码、学习资料&#xff0c;请关注源代码社区公众号(ydmsq666) from&#xff1a;https://blog.csdn.net/qq_34879948/article/details/80313690 升级android studio 3.0后&#xff0c;在创建新项目时会报一个异常&#xff0c…

客户端异常的处理

在我们的项目中&#xff0c;我们一般只会捕获我们自己能够想到的异常&#xff0c;但是这样就会导致一些无法捕捉到的异常被漏出去&#xff0c;因为我们不能确定它们会在什么地方出现&#xff0c;例如像NullPointerException&#xff0c;ClassCastException&#xff0c;IndexOut…

2021年6月大学英语六级作文

目录 1.第一套 2.第二套 3.第三套 1.第一套 中国高等教育∶Directions∶For this part&#xff0c; you are allowed 30 mimites to write an essay based on the chart below.You should start your essay with a brief description of the chart and comment on Chinas ac…

【人工智能】大模型平台新贵——文心千帆

个人主页&#xff1a;【&#x1f60a;个人主页】 &#x1f31e;热爱编程&#xff0c;热爱生活&#x1f31e; 文章目录 前言大模型平台文心千帆发布会推理能力模型微调 作用 前言 在不久的之前我们曾讨论过在ChatGPT爆火的大环境下&#xff0c;百度推出的“中国版ChatGPT”—文…

你如何看待百度的文心一言ERNIE Bot?心有所系,言出必行。

心有所系&#xff0c;言出必行。 排队中。。。 文心一言 百度全新一代知识增强大语言模型&#xff0c;文心大模型家族的新成员&#xff0c;能够与人对话互动&#xff0c;回答问题&#xff0c;协助创作&#xff0c;高效便捷地帮助人们获取信息、知识和灵感。 文心一言&#x…

Matcher: Segment Anything with One Shot Using All-Purpose Feature Matching 论文精读

Matcher: Segment Anything with One Shot Using All-Purpose Feature Matching 论文链接&#xff1a;[2305.13310] Matcher: Segment Anything with One Shot Using All-Purpose Feature Matching (arxiv.org) 代码链接&#xff1a;aim-uofa/Matcher: Matcher: Segment Anyt…

【问题记录】关于百度网盘客户端打不开

问题描述 双击桌面图标没有任何反应&#xff0c;用管理员模式也是没什么反应。 问题追踪 按ctrlshiftesc打开任务管理器&#xff0c;查看到百度网盘的一个升级程序在运行中&#xff0c;注意升级程序的网络一栏&#xff0c;表明该进程在下载东西&#xff0c;但是速度很慢 在…

百度网盘的最新插件(懂得都懂)

下面先给大家介绍一下油猴插件。 这个插件为什么叫油猴&#xff1f; 现在我们经常提到的油猴插件&#xff0c;常指Tampermonkey&#xff0c;但Tampermonkey翻译过来是叫篡改猴&#xff0c;为什么会叫油猴呢&#xff1f;原因是因为另一个插件Greasemonkey&#xff0c;它翻译过…

求和:1-1/2+1/3-...1/n(JAVA)

所用知识&#xff1a; 1.输入输出语句 2.数据类型的自动转换 3.for循环控制语句 4.if条件控制语句 5.赋值语句 6.判断语句 import java.util.Scanner; class samp_5{public static void main(String[] args){Scanner snew Scanner(System.in);System.out.print("请输入数…

函数 1+1/2+1/3+…1/n

题目&#xff1a; 编写函数&#xff0c;求11/21/3…1/n。要求在主函数中输入n的值&#xff0c;并输出结果 代码&#xff1a; #include<stdio.h> #include<stdlib.h> int main() {int n;scanf("%d",&n);float num0;for(int i0;i<n;i){num1.0/(i…

《红楼梦》各版本总结

本文主要参考洪邑四维馆传人的 [文章][1] 对《红楼梦》的各版本和《红楼梦》脂评本作了图形化的展示。 受小博主所见所识限制&#xff0c;其中错误之处在所难免&#xff0c;恭请不吝赐教。 [1]&#xff1a;https://www.jianshu.com/p/474ea4f59081

《红楼梦》的作者真的是曹雪芹吗?

原文链接 随狂风去 ​ 尚书令 等 19,569 人赞同了该回答 作者名字确实是叫曹雪芹&#xff0c;但此曹雪芹绝不是江宁织造府的那位“曹雪芹”。 谈这个问题&#xff0c;必须先认识到一点&#xff1a;曹雪芹这三个字它只是个笔名&#xff0c;红楼的作者&#xff0c;在真实历史中&a…

引用还是传值——被打脸后才发现多年的理解是错的

这是一个很基础的问题&#xff0c;如果你已经理解透彻了&#xff0c;其实可以不需要往下看&#xff08;如果理解没错的话&#xff09;&#xff0c;因为相信你已经知道了答案&#xff0c;本篇主要是解释给和我一样一直以来有这样误解的人&#xff0c;事实上这是一个简单的问题&a…

一个页面同时请求多个接口,超过6个请求之后,stallled时间过长

Network Timing 使用Chrome浏览器请求多个接口时发现前面的请求Network Timing中stalled时间明显较短&#xff0c;到第四个请求时stalled时间开始加长&#xff0c;超过6个请求之后&#xff0c;stallled时间过长&#xff0c;导致接口整体请求时间变长&#xff0c;页面渲染变慢。…

疫情期间再读三体——读后感

最近疫情持续的时间有点长&#xff0c;感觉在家挺无聊的&#xff0c;正好《我的三体--章北海传》完结了&#xff0c;这部动画真的是圆了三体迷的重现三体书中的场景和意境的的一个梦想。章北海是书中的一个重要人物&#xff0c;也是当前这部动漫的主角&#xff0c;他为人类文明…

实验十七 VLAN间的三层通信

实验十七 VLAN间的三层通信 配置要求&#xff1a; 通过三层交换机实现VLAN间互通 通过单臂路由实现VLAN间互通 网络拓扑图&#xff1a; 操作步骤&#xff1a; 一、 通过三层交换机实现VLAN间互通 1、配置交换机LSW1的接口为trunk接口&#xff0c;g0/0/1口允许vlan 10通过&#…

科幻小说《三体》读书笔记范文解读

科幻小说《三体》读书笔记范文解读&#xff1a; 《三体》这本书如雷贯耳了很久很久&#xff0c;只是听很多人说&#xff0c;文科生读不懂这本书&#xff0c;畏难情绪作祟&#xff0c;让我一直下不定决心来啃这块硬骨头。直到去年&#xff0c;朋友阿特说他买了这套书闲置在一边&…

DTI-ATS入门(2):DTI协议纵览

本文来自自学过程中所记的笔记&#xff0c;可能有不少错漏与胡言乱语&#xff0c;仅供参考&#xff0c;建议主要以ARM官方文件进行参考。原参考文档官网可下载&#xff0c;为了方便各位&#xff0c;我也上传了&#xff0c;需要的自取; 链接&#xff1a;https://pan.baidu.com/s…