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

前言

本篇文章主要介绍的是SpringBoot项目进行全局异常的处理。

SpringBoot全局异常准备

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

开发准备

环境要求

JDK:1.8

SpringBoot: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}

示例图:

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

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

使用POST方式进行请求

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

Body参数为:

{"id":1,"age":18}

返回参数为:

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

示例图:

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

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

使用PUT方式进行请求。

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

Body参数为:

{"id":1,"age":18}

返回参数为:

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

示例图:

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

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

使用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全局异常的处理项目工程地址:

https://github.com/xuwujing/springBoot-study/tree/master/springboot-exceptionHandler

SpringBoot整个集合的地址:

https://github.com/xuwujing/springBoot-study

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • springboot全局异常处理详解

    一.单个controller范围的异常处理 package com.xxx.secondboot.web; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import

  • SpringBoot全局异常与数据校验的方法

    异常处理是每个项目中都绕不开的话题,那么如何优雅的处理异常,是本文的话题.本文将结合SpringBoot框架一起和大家探讨下. 要思考的问题 在现在的前后端交互中,通常都规范了接口返回方式,如返回的接口状态(成功|失败)以及要返回的数据在那个字段取,或者说失败了以后提示信息从接口哪里返回,因此,如果想做全局异常,并且异常发生后能准确的返回给前端解析,那么需要异常发生时返回给前端的格式与正常失败场景的格式一致. 项目建立 利用idea 工具,很容易的搭建一个SpringBoot项目,要引入的mav

  • Spring Boot全局异常处理解析

    本文为大家分享了Spring Boot全局异常处理,供大家参考,具体内容如下 1.后台处理异常 a.引入thymeleaf依赖 <!-- thymeleaf模板插件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>

  • springboot结合全局异常处理实现登录注册验证

    在学校做一个校企合作项目,注册登录这一块需要对注册登录进行输入合法的服务器端验证,因为是前后端分离开发,所以要求返回JSON数据. 方法有很多,这觉得用全局异常处理比较容易上手 全局异常处理 首先来创建一个sprIngboot的web项目或模块,目录结构如下 实体类User.java @Data public class User { private String userName; private String passwold; } 实体类UserResult.java 把数据封装到这里返回

  • springboot springmvc抛出全局异常的解决方法

    springboot中抛出异常,springboot自带的是springmvc框架,这个就不多说了. springmvc统一异常解决方法这里要说明的是.只是结合了springboot的使用而已.直接上代码,有效有用的才是ok. 1.定义异常捕获 package com.example.rest.error; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.Exce

  • SpringBoot学习之全局异常处理设置(返回JSON)

    SpringBoot学习--全局异常处理设置(返回JSON) 需求 现在习惯使用ajax的方式发起请求,所以经常需要服务端返回一个json或者字符串. 控制全局的异常处理. 如果在单个方法中使用try,catch把方法包裹起来,工作量大,而且会异常的抛出而导致@Transactional注解的方法事务不会回滚. 说明 使用@ControllerAdvice注解 使用@ExceptionHandler注解 @ControllerAdvice 该注解是spring2.3以后新增的一个注解,主要是用来

  • 详解Spring Boot2 Webflux的全局异常处理

    本文首先将会回顾Spring 5之前的SpringMVC异常处理机制,然后主要讲解Spring Boot 2 Webflux的全局异常处理机制. SpringMVC的异常处理 Spring 统一异常处理有 3 种方式,分别为: 使用 @ExceptionHandler 注解 实现 HandlerExceptionResolver 接口 使用 @controlleradvice 注解 使用 @ExceptionHandler 注解 用于局部方法捕获,与抛出异常的方法处于同一个Controller类

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

    前言 本篇文章主要介绍的是SpringBoot项目进行全局异常的处理. SpringBoot全局异常准备 说明:如果想直接获取工程那么可以直接跳到底部,通过链接下载工程代码. 开发准备 环境要求 JDK:1.8 SpringBoot:1.5.17.RELEASE 首先还是Maven的相关依赖: <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.

  • SpringBoot如何优雅地处理全局异常详解

    前言 之前用springboot的时候,只知道捕获异常使用try{}catch,一个接口一个try{}catch,这也是大多数开发人员异常处理的常用方式,虽然屡试不爽,但会造成一个问题,就是一个Controller下面,满屏幕的try{}catch,看着一点都不优雅,一点都不符合小明的气质,憋了这么久,小明今天终于决定对所有异常实施统一处理的方案. 开发准备 JDK8.正常的springboot项目 代码编写 通用异常处理 其实Spring系列的项目全局异常处理方式早已存在,只不过我们一直忙于搬

  • yii2 开发api接口时优雅的处理全局异常的方法

    前言:个人觉得,学习或温习一套Web框架,在快速阅读一遍文档后,应从路由,控制器,请求/响应对象,数据模型(Logic,Dao,Entity),全局异常处理几个方面下手,这几项了解后,框架上手就游刃有余了.然后我比较喜欢在开工前整理好框架的全局异常处理,方便写 api时错误的统一响应. 在api接口的开发过程中,我们需要对用户数据进行严格的校验,防止非法输入对服务产生安全问题,在开发过程中,我比较喜欢即时的以抛出异常的方式中断请求的处理,并以全局异常处理器格式化处理后统一返回给客户端. 今天就把

  • 如何基于Springboot完成新增员工功能并设置全局异常处理器

    目录 1. 新增员工 1.1 需求分析 1.2 数据模型 1.3 程序执行流程 1.4 代码实现 2 全局异常处理 2.1 新增员工存在的问题 2.2 全局异常处理思路 2.3 全局异常处理器 2.4 全局异常处理器代码实现 2.5 测试 总结 1. 新增员工 1.1 需求分析 后台系统中可以管理员工信息,通过新增员工来添加后台系统用户.点击[添加员工]按钮跳转到新增页面,如下 当填写完表单信息, 点击"保存"按钮后, 会提交该表单的数据到服务端, 在服务端中需要接受数据, 然后将数据

  • springboot如何添加全局异常捕获类

    这篇文章主要介绍了springboot如何添加全局异常捕获类,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前几天工作中遇到一个项目,前后端分离的,作为后端开发专注开发接口.对于程序中出现的异常如果不进行处理,将报错信息直接返回到前端十=时不优雅的,因此需要对异常进行捕获和处理,但是每个接口都单独处理异常则显得代码十分臃肿,因此写了个异常捕获类对全局出现的异常进行统一处理. 1.如果程序中出现异常如下: @RestController publ

  • SpringBoot项目优雅的全局异常处理方式(全网最新)

    前言 在日常项目开发中,异常是常见的,但是如何更高效的处理好异常信息,让我们能快速定位到BUG,是很重要的,不仅能够提高我们的开发效率,还能让你代码看上去更舒服,SpringBoot的项目已经对有一定的异常处理了,但是对于我们开发者而言可能就不太合适了,因此我们需要对这些异常进行统一的捕获并处理. 一.全局异常处理方式一 SpringBoot中,@ControllerAdvice 即可开启全局异常处理,使用该注解表示开启了全局异常的捕获,我们只需在自定义一个方法使用@ExceptionHandl

  • SpringBoot优雅地实现全局异常处理的方法详解

    目录 前言 异常工具 异常处理 异常捕捉 前言 在前一节的学习中,慕歌带大家使用了全局结果集返回,通过使用全局结果集配置,优雅的返回后端数据,为前端的数据拿取提供了非常好的参考.同时通过不同的状态码返回,我们能够清晰的了解报错的位置,排除错误.如果大家有需要,可以使用我提供的的同一结果集以及状态码,并且可以使用全局异常拦截,实现异常的标准返回.接下来,我们一起来了解如何使用全局异常处理吧! 异常工具 先定义一个合适 的异常处理类,在之后的异常都会以这种格式返回前端,前端根据我们的异常进行自己的返

  • Springboot全局异常捕获及try catch区别解析

    今天在码代码的时候突然想到这个问题,觉得有点困惑.在网上也翻阅不少帖子其中有一个帖子给了我一个思路,其实也是解释了基础概念. 概念一:try catch 语句在try里面捕获到异常之后会进入catch子句内,执行catch内的代码. 概念二:springboot中的全局异常处理在捕获到异常之后,异常代码之后的语句是不会再运行了的 那么从我一开始思路理解开始,这个时候我觉得这2个的功能用处都是在代码出现异常情况的时候对其进行处理,且向调用者(或者就是用户)返回一个友好的提示,那这2个有啥区别呢,重

  • SpringBoot配置系统全局异常映射处理

    目录 一.异常分类 1.业务异常 2.系统异常 二.解决应用启动后404界面 1.引入页面Jar包 2.自定义首页接口 3.首页界面 4.运行效果 三.SpringBoot2.0中异常处理 1.项目结构图 2.自定义业务异常类 3.自定义异常描述对象 4.统一异常处理格式 5.简单的测试接口 四.源代码地址 一.异常分类 这里的异常分类从系统处理异常的角度看,主要分类两类:业务异常和系统异常. 1.业务异常 业务异常主要是一些可预见性异常,处理业务异常,用来提示用户的操作,提高系统的可操作性.

随机推荐