0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

关于SpringBoot如何优雅的全局异常处理

电子工程师 来源:博客 作者:虚无境 2021-05-31 14:25 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

SpringBoot全局异常准备说明:如果想直接获取工程那么可以直接跳到底部,通过链接下载工程代码。

开发准备

环境要求JDK:1.8SpringBoot:1.5.17.RELEASE

首先还是Maven的相关依赖:

《properties》

《project.build.sourceEncoding》UTF-8《/project.build.sourceEncoding》

java.version》1.8《/java.version》

《maven.compiler.source》1.8《/maven.compiler.source》

《maven.compiler.target》1.8《/maven.compiler.target》

《/properties》

《parent》

《groupId》org.springframework.boot《/groupId》

《artifactId》spring-boot-starter-parent《/artifactId》

《version》1.5.17.RELEASE《/version》

《relativePath /》

《/parent》

《dependencies》

《!-- Spring Boot Web 依赖 核心 --》

《dependency》

《groupId》org.springframework.boot《/groupId》

《artifactId》spring-boot-starter-web《/artifactId》

《/dependency》

《!-- Spring Boot Test 依赖 --》

《dependency》

《groupId》org.springframework.boot《/groupId》

《artifactId》spring-boot-starter-test《/artifactId》

《scope》test《/scope》

《/dependency》

《dependency》

《groupId》com.alibaba《/groupId》

《artifactId》fastjson《/artifactId》

《version》1.2.41《/version》

《/dependency》

《/dependencies》

配置文件这块基本不需要更改,全局异常的处理只需在代码中实现即可。

代码编写

SpringBoot的项目已经对有一定的异常处理了,但是对于我们开发者而言可能就不太合适了,因此我们需要对这些异常进行统一的捕获并处理。SpringBoot中有一个ControllerAdvice的注解,使用该注解表示开启了全局异常的捕获,我们只需在自定义一个方法使用ExceptionHandler注解然后定义捕获异常的类型即可对这些捕获的异常进行统一的处理。

我们根据下面的这个示例来看该注解是如何使用吧。

示例代码:

@ControllerAdvice

public class MyExceptionHandler {

@ExceptionHandler(value =Exception.class)

public String exceptionHandler(Exception e){

System.out.println(“未知异常!原因是:”+e);

return e.getMessage();

}

}

上述的示例中,我们对捕获的异常进行简单的二次处理,返回异常的信息,虽然这种能够让我们知道异常的原因,但是在很多的情况下来说,可能还是不够人性化,不符合我们的要求。那么我们这里可以通过自定义的异常类以及枚举类来实现我们想要的那种数据吧。

自定义基础接口

首先定义一个基础的接口类,自定义的错误描述枚举类需实现该接口。代码如下:

public interface BaseErrorInfoInterface {

/** 错误码*/

String getResultCode();

/** 错误描述*/

String getResultMsg();

}

自定义枚举类

然后我们这里在自定义一个枚举类,并实现该接口。代码如下:

public enum CommonEnum implements BaseErrorInfoInterface {

// 数据操作错误定义

SUCCESS(“200”, “成功!”),

BODY_NOT_MATCH(“400”,“请求的数据格式不符!”),

SIGNATURE_NOT_MATCH(“401”,“请求的数字签名不匹配!”),

NOT_FOUND(“404”, “未找到该资源!”),

INTERNAL_SERVER_ERROR(“500”, “服务器内部错误!”),

SERVER_BUSY(“503”,“服务器正忙,请稍后再试!”)

;

/** 错误码 */

private String resultCode;

/** 错误描述 */

private String resultMsg;

CommonEnum(String resultCode, String resultMsg) {

this.resultCode = resultCode;

this.resultMsg = resultMsg;

}

@Override

public String getResultCode() {

return resultCode;

}

@Override

public String getResultMsg() {

return resultMsg;

}

}

自定义异常类

然后我们在来自定义一个异常类,用于处理我们发生的业务异常。代码如下:

public class BizException extends RuntimeException {

private static final long serialVersionUID = 1L;

/**

* 错误码

*/

protected String errorCode;

/**

* 错误信息

*/

protected String errorMsg;

public BizException() {

super();

}

public BizException(BaseErrorInfoInterface errorInfoInterface) {

super(errorInfoInterface.getResultCode());

this.errorCode = errorInfoInterface.getResultCode();

this.errorMsg = errorInfoInterface.getResultMsg();

}

public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {

super(errorInfoInterface.getResultCode(), cause);

this.errorCode = errorInfoInterface.getResultCode();

this.errorMsg = errorInfoInterface.getResultMsg();

}

public BizException(String errorMsg) {

super(errorMsg);

this.errorMsg = errorMsg;

}

public BizException(String errorCode, String errorMsg) {

super(errorCode);

this.errorCode = errorCode;

this.errorMsg = errorMsg;

}

public BizException(String errorCode, String errorMsg, Throwable cause) {

super(errorCode, cause);

this.errorCode = errorCode;

this.errorMsg = errorMsg;

}

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 String getMessage() {

return errorMsg;

}

@Override

public Throwable fillInStackTrace() {

return this;

}

}

自定义数据格式

顺便这里我们定义一下数据的传输格式。代码如下:

public class ResultBody {

/**

* 响应代码

*/

private String code;

/**

* 响应消息

*/

private String message;

/**

* 响应结果

*/

private Object result;

public ResultBody() {

}

public ResultBody(BaseErrorInfoInterface errorInfo) {

this.code = errorInfo.getResultCode();

this.message = errorInfo.getResultMsg();

}

public String getCode() {

return code;

}

public void setCode(String code) {

this.code = code;

}

public String getMessage() {

return message;

}

public void setMessage(String message) {

this.message = message;

}

public Object getResult() {

return result;

}

public void setResult(Object result) {

this.result = result;

}

/**

* 成功

*

* @return

*/

public static ResultBody success() {

return success(null);

}

/**

* 成功

* @param data

* @return

*/

public static ResultBody success(Object data) {

ResultBody rb = new ResultBody();

rb.setCode(CommonEnum.SUCCESS.getResultCode());

rb.setMessage(CommonEnum.SUCCESS.getResultMsg());

rb.setResult(data);

return rb;

}

/**

* 失败

*/

public static ResultBody error(BaseErrorInfoInterface errorInfo) {

ResultBody rb = new ResultBody();

rb.setCode(errorInfo.getResultCode());

rb.setMessage(errorInfo.getResultMsg());

rb.setResult(null);

return rb;

}

/**

* 失败

*/

public static ResultBody error(String code, String message) {

ResultBody rb = new ResultBody();

rb.setCode(code);

rb.setMessage(message);

rb.setResult(null);

return rb;

}

/**

* 失败

*/

public static ResultBody error( String message) {

ResultBody rb = new ResultBody();

rb.setCode(“-1”);

rb.setMessage(message);

rb.setResult(null);

return rb;

}

@Override

public String toString() {

return JSONObject.toJSONString(this);

}

}

自定义全局异常处理类

最后我们在来编写一个自定义全局异常处理的类。代码如下:

@ControllerAdvice

public class GlobalExceptionHandler {

private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

/**

* 处理自定义的业务异常

* @param req

* @param e

* @return

*/

@ExceptionHandler(value = BizException.class)

@ResponseBody

public ResultBody bizExceptionHandler(HttpServletRequest req, BizException e){

logger.error(“发生业务异常!原因是:{}”,e.getErrorMsg());

return ResultBody.error(e.getErrorCode(),e.getErrorMsg());

}

/**

* 处理空指针的异常

* @param req

* @param e

* @return

*/

@ExceptionHandler(value =NullPointerException.class)

@ResponseBody

public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e){

logger.error(“发生空指针异常!原因是:”,e);

return ResultBody.error(CommonEnum.BODY_NOT_MATCH);

}

/**

* 处理其他异常

* @param req

* @param e

* @return

*/

@ExceptionHandler(value =Exception.class)

@ResponseBody

public ResultBody exceptionHandler(HttpServletRequest req, Exception e){

logger.error(“未知异常!原因是:”,e);

return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);

}

}

因为这里我们只是用于做全局异常处理的功能实现以及测试,所以这里我们只需在添加一个实体类和一个控制层类即可。

实体类

又是万能的用户表 (^▽^)

代码如下:

public class User implements Serializable{

private static final long serialVersionUID = 1L;

/** 编号 */

private int id;

/** 姓名 */

private String name;

/** 年龄 */

private int age;

public User(){

}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public String toString() {

return JSONObject.toJSONString(this);

}

}

Controller 控制层

控制层这边也比较简单,使用Restful风格实现的CRUD功能,不同的是这里我故意弄出了一些异常,好让这些异常被捕获到然后处理。这些异常中,有自定义的异常抛出,也有空指针的异常抛出,当然也有不可预知的异常抛出(这里我用类型转换异常代替),那么我们在完成代码编写之后,看看这些异常是否能够被捕获处理成功吧!

代码如下:

@RestController

@RequestMapping(value = “/api”)

public class UserRestController {

@PostMapping(“/user”)

public boolean insert(@RequestBody User user) {

System.out.println(“开始新增。。。”);

//如果姓名为空就手动抛出一个自定义的异常!

if(user.getName()==null){

throw new BizException(“-1”,“用户姓名不能为空!”);

}

return true;

}

@PutMapping(“/user”)

public boolean update(@RequestBody User user) {

System.out.println(“开始更新。。。”);

//这里故意造成一个空指针的异常,并且不进行处理

String str=null;

str.equals(“111”);

return true;

}

@DeleteMapping(“/user”)

public boolean delete(@RequestBody User user) {

System.out.println(“开始删除。。。”);

//这里故意造成一个异常,并且不进行处理

Integer.parseInt(“abc123”);

return true;

}

@GetMapping(“/user”)

public List《User》 findByUser(User user) {

System.out.println(“开始查询。。。”);

List《User》 userList =new ArrayList《》();

User user2=new User();

user2.setId(1L);

user2.setName(“xuwujing”);

user2.setAge(18);

userList.add(user2);

return userList;

}

}

App 入口

和普通的SpringBoot项目基本一样。

代码如下:

@SpringBootApplication

public class App

{

public static void main( String[] args )

{

SpringApplication.run(App.class, args);

System.out.println(“程序正在运行。。。”);

}

}

功能测试

我们成功启动该程序之后,使用Postman工具来进行接口测试。

首先进行查询,查看程序正常运行是否ok,使用GET 方式进行请求。

GET http://localhost:8181/api/user

”返回参数为:

{“id”:1,“name”:“xuwujing”,“age”:18}

”示例图:

7922d7ee-c1d5-11eb-9e57-12bb97331649.png

可以看到程序正常返回,并没有因自定义的全局异常而影响。

然后我们再来测试下自定义的异常是否能够被正确的捕获并处理。

使用POST方式进行请求

POST http://localhost:8181/api/user

”Body参数为:

{“id”:1,“age”:18}

”返回参数为:

{“code”:“-1”,“message”:“用户姓名不能为空!”,“result”:null}

”示例图:

796e0dd6-c1d5-11eb-9e57-12bb97331649.png

可以看出将我们抛出的异常进行数据封装,然后将异常返回出来。

然后我们再来测试下空指针异常是否能够被正确的捕获并处理。在自定义全局异常中,我们除了定义空指针的异常处理,也定义最高级别之一的Exception异常,那么这里发生了空指针异常之后,它是回优先使用哪一个呢?这里我们来测试下。

使用PUT方式进行请求。

PUT http://localhost:8181/api/user

”Body参数为:

{“id”:1,“age”:18}

”返回参数为:

{“code”:“400”,“message”:“请求的数据格式不符!”,“result”:null}

”示例图:

79859b90-c1d5-11eb-9e57-12bb97331649.png

我们可以看到这里的的确是返回空指针的异常护理,可以得出全局异常处理优先处理子类的异常。

那么我们在来试试未指定其异常的处理,看该异常是否能够被捕获。

使用DELETE方式进行请求。

DELETE http://localhost:8181/api/user

”Body参数为:

{“id”:1}

”返回参数为:

{“code”:“500”,“message”:“服务器内部错误!”,“result”:null}

这里可以看到它使用了我们在自定义全局异常处理类中的Exception异常处理的方法。到这里,测试就结束了。顺便再说一下,自义定全局异常处理除了可以处理上述的数据格式之外,也可以处理页面的跳转,只需在新增的异常方法的返回处理上填写该跳转的路径并不使用ResponseBody 注解即可。

细心的同学也许发现了在GlobalExceptionHandler类中使用的是ControllerAdvice注解,而非RestControllerAdvice注解,如果是用的RestControllerAdvice注解,它会将数据自动转换成JSON格式,这种于Controller和RestController类似,所以我们在使用全局异常处理的之后可以进行灵活的选择处理。

其它关于SpringBoot优雅的全局异常处理的文章就讲解到这里了,如有不妥,欢迎指正!

项目地址

SpringBoot全局异常的处理项目工程地址//github.com/xuwujing/springBoot-study/tree/master/springboot-exceptionHandler

编辑;jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 代码
    +关注

    关注

    30

    文章

    4986

    浏览量

    74644
  • JDK
    JDK
    +关注

    关注

    0

    文章

    83

    浏览量

    17219
  • PUT
    PUT
    +关注

    关注

    0

    文章

    6

    浏览量

    6471
  • SpringBoot
    +关注

    关注

    0

    文章

    179

    浏览量

    746

原文标题:看看人家SpringBoot的全局异常处理多么优雅...

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    如何使用PicoScope示波器调试异常信号

    高效的隔离和调试分析信号异常事件是工程师在产品研发过程中面临的常见挑战。PicoScope 7软件通过三个强大且互补的工具帮助工程师应对这一挑战:模拟余晖模式用于观察异常,智能触发用于精确捕获异常,以及模板容限测试用于捕获
    的头像 发表于 05-27 09:31 2536次阅读
    如何使用PicoScope示波器调试<b class='flag-5'>异常</b>信号

    深入剖析ARM64异常处理:开发者必须掌握的底层核心逻辑

    在ARM64架构的开发领域,异常处理绝非单纯的理论知识点,而是直接决定系统稳定性、调试效率和功能实现的关键技术。无论是嵌入式开发、Linux内核移植,还是驱动开发与芯片调试,理解异常发生后CPU
    的头像 发表于 12-24 07:05 1540次阅读
    深入剖析ARM64<b class='flag-5'>异常</b><b class='flag-5'>处理</b>:开发者必须掌握的底层核心逻辑

    如何搞定嵌入式 C语言中的全局变量问题?

    大家好,今天分享一篇关于嵌入式C编程中全局变量问题的文章。希望对大家有所启发。 嵌入式特别是单片机os-less的程序,最易范的错误是全局变量满天飞。 这个现象在早期汇编转型过来的程序员以及初学者
    发表于 12-16 06:54

    使用setjmp及longjmp函数处理异常

    使用setjmp和longjmp函数:这是一种用于实现非局部跳转的方法,就是在程序中设置一个跳转点,并在某些情况下跳转到该跳转点,从而绕过中间的一些代码或函数。这样可以在某些情况下模拟异常处理的效果
    发表于 12-11 08:00

    检查函数返回值的错误和异常处理方法

    检查函数返回值:这是最常见也最基本的错误处理异常处理方法,就是在调用一个函数后,检查其返回值是否符合预期或是否表示出错或失败。如果出错或失败,则根据返回值或者全局变量errno(定义
    发表于 12-11 06:48

    请问C语言开发单片机为什么大多数都采用全局变量的形式?

    C语言代码,大多数都是使用全局变量,也就是用很多函数来操作这些变量,比如函数1把一个全局变量经过一系列复杂的算法计算后改变了这个全局变量的值,然后函数2再拿着函数1处理过的这个
    发表于 12-04 07:47

    C++程序异常处理机制

    1、什么是异常处理? 有经验的朋友应该知道,在正常的C和C++编程过程中难免会碰到程序不按照原本设计运行的情况。 最常见的有除法分母为零,数组越界,内存分配失效、打开相应文件失败等等。 一个程序
    发表于 12-02 07:12

    如何使用SpringBoot、Vue2.0、MySQL开发一套云诊所系统?

    SpringBoot是Java领域非常流行的快速开发框架,提供了丰富的生态和自动化配置,适合构建微服务和单体应用。 它可以很好地处理业务逻辑、数据持久化、安全性(Spring Security)和API接口
    的头像 发表于 11-27 16:02 507次阅读
    如何使用<b class='flag-5'>SpringBoot</b>、Vue2.0、MySQL开发一套云诊所系统?

    线路保护光纤通道异常处理方法

    通道异常的 常见原因、处理步骤及预防措施 ,帮助运维人员快速定位问题,提升故障处理效率。 广州邮科光纤线路保护系统 一、光纤通道异常的常见表现 当线路保护光纤通道出现
    的头像 发表于 11-17 10:01 1873次阅读
    线路保护光纤通道<b class='flag-5'>异常</b><b class='flag-5'>处理</b>方法

    医院随访管理系统源码,三级随访系统源码,Java+Springboot,Vue,Ant-Design+MySQL5

    Java版随访系统源码,医院随访管理系统源码,三级随访系统源码,B/S前后端分离架构,自主版权,落地案例。 技术框架:Java+Springboot,Vue,Ant-Design+MySQL5 开发
    的头像 发表于 11-08 14:48 904次阅读
    医院随访管理系统源码,三级随访系统源码,Java+<b class='flag-5'>Springboot</b>,Vue,Ant-Design+MySQL5

    求助,关于全局中断使能的问题求解

    各位朋友大家好,我最近在使用蜂鸟的板子进行开发时,遇到了这样的问题:我的程序每次运行到使能全局中断的时候,就像进入了死循环一样,出不去了,如上图,首先先打印“GI_EN begin!”这里是可以
    发表于 11-07 06:37

    如何处理电能质量在线监测装置时钟模块自动同步异常的情况?

    处理电能质量在线监测装置时钟模块自动同步异常,需遵循 “ 先定位异常类型→再分步骤排查(从软到硬、从简到繁)→最后验证恢复 ” 的逻辑,针对 PTP、GPS、NTP 三种主流同步方式的差异,采取
    的头像 发表于 10-27 10:16 1528次阅读

    如何利用AI算法进行装置数据的异常检测?

    利用 AI 算法进行装置数据异常检测,需结合工业装置的数据特性(如实时性、多源性、强时序性、噪声干扰)和业务需求(如故障预警、安全合规、工艺优化),通过 “数据预处理 - 算法选型 - 模型部署
    的头像 发表于 09-05 15:27 2507次阅读
    如何利用AI算法进行装置数据的<b class='flag-5'>异常</b>检测?

    碳化硅衬底 TTV 厚度测量数据异常的快速诊断与处理流程

    摘要 本文针对碳化硅衬底 TTV 厚度测量中出现的数据异常问题,系统分析异常类型与成因,构建科学高效的快速诊断流程,并提出针对性处理方法,旨在提升数据异常
    的头像 发表于 08-14 13:29 1500次阅读
    碳化硅衬底 TTV 厚度测量数据<b class='flag-5'>异常</b>的快速诊断与<b class='flag-5'>处理</b>流程

    机器学习异常检测实战:用Isolation Forest快速构建无标签异常检测系统

    本文转自:DeepHubIMBA无监督异常检测作为机器学习领域的重要分支,专门用于在缺乏标记数据的环境中识别异常事件。本文深入探讨异常检测技术的理论基础与实践应用,通过IsolationForest
    的头像 发表于 06-24 11:40 1703次阅读
    机器学习<b class='flag-5'>异常</b>检测实战:用Isolation Forest快速构建无标签<b class='flag-5'>异常</b>检测系统