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

    文章

    4555

    浏览量

    66769
  • JDK
    JDK
    +关注

    关注

    0

    文章

    77

    浏览量

    16489
  • PUT
    PUT
    +关注

    关注

    0

    文章

    5

    浏览量

    6275
  • SpringBoot
    +关注

    关注

    0

    文章

    172

    浏览量

    106

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

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

收藏 人收藏

    评论

    相关推荐

    优雅停机是什么?SpringBoot+Nacos+k8s实现优雅停机

    优雅停机是什么?网上说的优雅下线、无损下线,都是一个意思。
    的头像 发表于 02-20 10:00 502次阅读
    <b class='flag-5'>优雅</b>停机是什么?<b class='flag-5'>SpringBoot</b>+Nacos+k8s实现<b class='flag-5'>优雅</b>停机

    Service层的异常处理

    一般初学者学习编码和[错误处理]时,先知道[编程语言]有一种处理错误的形式或约定(如Java就抛异常),然后就开始用这些工具。但却忽视这问题本质:「处理错误是为了写正确程序」 。
    的头像 发表于 01-08 11:29 251次阅读

    直流屏系统的操作、查询及异常处理方法

    直流屏系统的操作、查询及异常处理方法
    的头像 发表于 12-15 10:29 386次阅读
    直流屏系统的操作、查询及<b class='flag-5'>异常</b><b class='flag-5'>处理</b>方法

    springboot全局配置文件有几种

    Spring Boot是一种快速开发框架,其通过提供配置文件来实现对应用程序的配置。全局配置文件在Spring Boot中起着非常重要的作用,可以用于配置各种不同的属性,包括数据库连接、日志级别
    的头像 发表于 12-03 15:28 490次阅读

    一个注解搞定SpringBoot接口防刷

    技术要点:springboot的基本知识,redis基本操作,
    的头像 发表于 11-28 10:46 221次阅读

    变压器油位异常的原因和处理方法是什么?

    变压器油位异常的原因和处理方法是什么? 变压器油位异常是指变压器内部绝缘油的液位偏离正常范围,常见的异常情况有油位过高或过低。油位异常可能会
    的头像 发表于 11-23 15:34 1816次阅读

    javaweb和springboot能一起用吗

    JavaWeb 和 SpringBoot 是两种针对 Java 程序开发的框架,它们可以在一起使用。在本文中,我将详细介绍 JavaWeb 和 SpringBoot 的关系,并探讨如何结合使用这两个
    的头像 发表于 11-16 10:54 892次阅读

    SpringBoot 连接ElasticSearch的使用方式

    SpringBoot,今天我们就以 SpringBoot 整合 ElasticSearch 为例,给大家详细的介绍 ElasticSearch 的使用! SpringBoot 连接 ElasticSearch,主流
    的头像 发表于 10-09 10:35 426次阅读

    如何有效的处理空指针异常

    地遇到这个问题。 那么我们应该如何有效且优雅处理空指针异常呢? 下面了不起将详细的介绍这个处理方案。 1、什么是空指针异常? 空指针
    的头像 发表于 09-30 10:25 1011次阅读

    arm处理器的异常模式包括哪些

    arm处理器的异常模式包括哪些 ARM处理器的异常模式是其操作系统和应用程序中最重要的方面之一。异常就是
    的头像 发表于 09-05 16:22 2308次阅读

    ARMv8-M异常处理手册

    ARMv8-M异常模型描述了处理器如何响应异常与每个异常关联的属性,例如其优先级和异常返回行为。通常有一个
    发表于 08-02 06:09

    springboot统一异常处理

    限流对于一个微服务架构系统来说具有非常重要的意义,否则其中的某个微服务将成为整个系统隐藏的雪崩因素,为什么这么说?
    的头像 发表于 07-25 16:11 427次阅读
    <b class='flag-5'>springboot</b>统一<b class='flag-5'>异常</b><b class='flag-5'>处理</b>

    SpringMVC 如何优雅处理各种异常

    那有没有一种方案,既不需要跟Controller耦合,也可以将定义的 异常处理器 应用到所有控制器呢?所以注解@ControllerAdvice出现了,简单的说,该注解可以把异常处理
    发表于 05-29 16:00 326次阅读
    SpringMVC 如何<b class='flag-5'>优雅</b>的<b class='flag-5'>处理</b>各种<b class='flag-5'>异常</b>?

    如何优雅处理异常

    Java 语言按照错误严重性,从 throwale 根类衍生出 Error 和 Exception 两大派系。
    的头像 发表于 05-24 16:20 243次阅读

    关于Jenkins+docker+springboot一键自动部署项目

    本文章实现最简单全面的Jenkins+docker+springboot 一键自动部署项目,步骤齐全,少走坑路。
    的头像 发表于 05-16 08:59 599次阅读
    <b class='flag-5'>关于</b>Jenkins+docker+<b class='flag-5'>springboot</b>一键自动部署项目