热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

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

这篇文章主要给大家介绍了关于SpringBoot如何优雅的处理全局异常的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用SpringBoot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

前言

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

SpringBoot全局异常准备

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

开发准备

环境要求

JDK:1.8

SpringBoot:1.5.17.RELEASE

首先还是Maven的相关依赖:

 
 UTF-8
 1.8
 1.8
 1.8
 
 
 org.springframework.boot
 spring-boot-starter-parent
 1.5.17.RELEASE
 
 
 
 
 
  org.springframework.boot
  spring-boot-starter-web
 
 
 
  org.springframework.boot
  spring-boot-starter-test
  test
 

 
  com.alibaba
  fastjson
  1.2.41
 
 

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

代码编写

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 findByUser(User user) {
  System.out.println("开始查询...");
  List 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

总结

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


推荐阅读
  • REST与RPC:选择哪种API架构风格?
    在探讨REST与RPC这两种API架构风格的选择时,本文首先介绍了RPC(远程过程调用)的概念。RPC允许客户端通过网络调用远程服务器上的函数或方法,从而实现分布式系统的功能调用。相比之下,REST(Representational State Transfer)则基于资源的交互模型,通过HTTP协议进行数据传输和操作。本文将详细分析两种架构风格的特点、适用场景及其优缺点,帮助开发者根据具体需求做出合适的选择。 ... [详细]
  • REST API 时代落幕,GraphQL 持续引领未来
    尽管REST API已广泛使用多年,但在深入了解GraphQL及其解决的核心问题后,我深感其将引领未来的API设计趋势。GraphQL不仅提高了数据查询的效率,还增强了灵活性和性能,有望成为API开发的新标准。 ... [详细]
  • 本文介绍了 Go 语言中的高性能、可扩展、轻量级 Web 框架 Echo。Echo 框架简单易用,仅需几行代码即可启动一个高性能 HTTP 服务。 ... [详细]
  • 技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统
    技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统 ... [详细]
  • 本文深入探讨了ASP.NET Web API与RESTful架构的设计与实现。ASP.NET Web API 是一个强大的框架,能够简化HTTP服务的开发,使其能够广泛支持各种客户端设备。通过详细分析其核心原理和最佳实践,本文为开发者提供了构建高效、可扩展且易于维护的Web服务的指导。此外,还讨论了如何利用RESTful原则优化API设计,确保系统的灵活性和互操作性。 ... [详细]
  • FastRequest1.1.4正式发布,优化了诸多操作的交互和用户体验,更新内容如下:curlsupport(支持curl拷贝)supportaddparamstoheaderfrom(支持了将参数从response添加到header)supportprettyandraw ... [详细]
  • restful是这些年的高频词汇了,各大互联网公司也都纷纷推出了自己的restfulapi,其实restful和thrift,grpc类似,就是一种协议,但是这种协议有点特殊的就是 ... [详细]
  • PHP-Casbin v3.20.0 已经发布,这是一个使用 PHP 语言开发的轻量级开源访问控制框架,支持多种访问控制模型,包括 ACL、RBAC 和 ABAC。新版本在性能上有了显著的提升。 ... [详细]
  • MongoDB核心概念详解
    本文介绍了NoSQL数据库的概念及其应用场景,重点解析了MongoDB的基本特性、数据结构以及常用操作。MongoDB是一个高性能、高可用且易于扩展的文档数据库系统。 ... [详细]
  • 小程序的授权和登陆
    小程序的授权和登陆 ... [详细]
  • 在OpenShift上部署基于MongoDB和Node.js的多层应用程序
    本文档详细介绍了如何在OpenShift 4.x环境中部署一个包含MongoDB数据库和Node.js后端及前端的多层应用程序。通过逐步指导,读者可以轻松完成整个部署过程。 ... [详细]
  • 【实例简介】本文详细介绍了如何在PHP中实现微信支付的退款功能,并提供了订单创建类的完整代码及调用示例。在配置过程中,需确保正确设置相关参数,特别是证书路径应根据项目实际情况进行调整。为了保证系统的安全性,存放证书的目录需要设置为可读权限。值得注意的是,普通支付操作无需证书,但在执行退款操作时必须提供证书。此外,本文还对常见的错误处理和调试技巧进行了说明,帮助开发者快速定位和解决问题。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 本文深入探讨了在Spring Boot中处理RESTful风格的表单请求的方法,包括请求参数处理、请求映射以及RESTful设计原则的应用。文章详细介绍了如何利用HTTP动词(如GET、POST、PUT、DELETE)来操作资源,并结合Spring Boot的注解(如@GetMapping、@PostMapping等)实现高效、清晰的请求处理逻辑。通过实例分析,展示了如何在实际项目中应用这些技术,提高开发效率和代码可维护性。 ... [详细]
  • 数据结构与算法:HyperLogLog 统计、布隆过滤器应用、缓存机制挑战及解决方案、Redis 性能优化与监控、哨兵模式、版本控制工具 Git
    本文探讨了数据结构与算法在实际应用中的多个方面。首先介绍了HyperLogLog算法,用于高效地进行基数统计,能够准确估算大规模数据集中的唯一元素数量。接着讨论了布隆过滤器的应用,该过滤器在空间效率和查询速度上具有显著优势,适用于大数据场景下的快速成员检测。此外,文章分析了缓存机制面临的挑战及其解决方案,包括LRU和LFU等策略,并详细阐述了Redis的性能优化与监控方法,如使用哨兵模式实现高可用性。最后,介绍了版本控制工具Git的基本操作和最佳实践,帮助开发者有效管理代码版本。 ... [详细]
author-avatar
久久影视001
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有