在互联网时代,我们所开发的应用大多是直面用户的,程序中的任何一点小疏忽都可能导致用户的流失,而程序出现异常往往又是不可避免的,那该如何减少程序异常对用户体验的影响呢?其实方法很简单,对异常进行捕获,然后给予相应的处理即可。但实现的方式却有好多种,例如:

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

像这种标准的 try-catch 是可以解决问题,但如果让你在每个接口实现里面都 try-catch 一下,我想你应该是不太愿意的。那么下面来介绍下 SpringBoot 为我们提供的处理方式。

1. ErrorController 应用

首先,我们来模拟一下,出现异常的场景,方式比较简单,直接在正常的代码里面抛出一个异常即可。

在上面的示例中,调用接口时,出现了异常,但客户端却收到一个相对正常的响应,这是因为 SpringBoot 默认提供了一个 /error 的映射,该映射被注册为 Servlet 容器中的一个全局错误页面用来合理处理所有的异常情况。但示例中的响应报文不符合我们定义的数据规范,想要使其满足自己的数据规范,可以自己定义一个新的 ErrorController,代码如下:

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class FundaErrorController implements ErrorController {@Overridepublic String getErrorPath() {return "/error";}@RequestMapping@ResponseBodypublic Result doHandleError() {return new Result(ResultCode.WEAK_NET_WORK);}
}

当我们再次访问该接口的时候会返回:

{"code": -1,"msg": "网络异常,请稍后重试","data": null
}

2. ExceptionHandler 应用

熟悉 SpringMVC 的人应该都知道 @ExceptionHandler 这个注解,在 SpringBoot 里面,我们同样可以使用它来做异常捕获。

2.1. 单一 Controller 异常处理

这种方式使用场景较少,但作为学习 @ExceptionHandler 入门示例还是非常不错的,直接在对应的 Controller 里面增加一个异常处理的方法,并使用 @ExceptionHandler 标识它即可。

@ExceptionHandler(Exception.class)
public Result handleException() {return new Result(ResultCode.WEAK_NET_WORK);
}

客户端得到的效果与使用 ErrorController 完全一致,但对于服务端来说却不太一样,如果仔细观察这两种方式的日志输出的话,会发现使用 ErrorController 时,后台会打印出异常堆栈信息,而使用 @ExceptionHandler 却不会,这是因为这两种处理方式的流程存在着本质的差别。

  1. ErrorController: 调用 UserController 抛出异常时,自身没有做任何处理,所以会打印出堆栈信息,但这个异常会被 Servlet 容器捕捉到,Servlet 容器再将请求转发给注册好的异常处理映射 /error 做处理,客户端收到的实际是 ErrorController 的处理结果,而不是 UserController 的。

  2. ExceptionHandler: 异常的处理方法直接被定义在 UserController 里面,也就是说,在异常抛出的时候,UserController 会使用自己的方法去做异常处理,而不会抛出给 Servlet 容器,所以这个地方没有打印堆栈信息。

如果想要在后台添加堆栈信息的输出也非常简单,只需要将该异常作为一个参数传递给异常处理方法,然后在处理方法里面做相应的操作即可。

@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {e.printStackTrace();return new Result(ResultCode.WEAK_NET_WORK);
}

2.2. 父级 Controller 异常处理

项目的往往存在着多个 Controller,而它们在异常处理方面有存在着很多的共性,这样就不太适合在每一个 Controller 里面都编写一个对应的异常处理方法。可以将异常处理方法向上挪移到父类中,然后所有的 Controller 统一继承父类即可。

定义父类 BaseController:

public class BaseController {@ExceptionHandler(Exception.class)public Result handleException(Exception e) {e.printStackTrace();return new Result(ResultCode.WEAK_NET_WORK);}}

UserController 通过继承 BaseController 完成异常处理:

@RestController
@RequestMapping("/sys/user")
public class UserController extends BaseController {...
}

2.3. Advice 异常处理

对于使用父级 Controller 完成异常处理也有着它自己的缺点,那就是代码耦合严重,一旦哪天忘记继承 BaseController,异常又会直达客户了。想要解除这种耦合关系,可以使用 @ControllerAdvice 来协助处理。

@ControllerAdvice
@ResponseBody
public class ExceptionHandlerAdvice {@ExceptionHandler(Exception.class)public Result handleException(Exception e) {e.printStackTrace();return new Result(ResultCode.WEAK_NET_WORK);}}

3. 多类别异常处理

实际的开发场景中,异常是区分很多类别的,不同类别的异常需要给用户不同的反馈。例如,在 SpringBoot实战 之 数据交互篇 中有使用到注解式参数校验,但校验不通过原因并没有以有效的方式告之给前端应用。下面我们通过上面提到的异常处理方式来完成这个功能:

首先,在 ResultCode 类中定义好 参数错误 的 code,代码如下:

PARAMETER_ERROR(10101, "参数错误")

在 ExceptionHandlerAdvice 中添加对应的异常处理方法:

@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleIllegalParamException(MethodArgumentNotValidException e) {List<ObjectError> errors = e.getBindingResult().getAllErrors();String tips = "参数不合法";if (errors.size() > 0) {tips = errors.get(0).getDefaultMessage();}Result result = new Result(ResultCode.PARAMETER_ERROR);result.setMsg(tips);return result;
}

  

当应用程序抛出 MethodArgumentNotValidException 时,会精确匹配到该方法,在方法里面会获取到校验结果,并将所有校验错误中的第一条返回给前端应用。

这样的话,就可以在 ExceptionHandlerAdvice 里面添加各种各样的异常处理方法,以适合不同的应用场景。

项目的 github 地址:https://github.com/qchery/funda

原文链接:http://blog.csdn.net/chinrui/article/details/71036544

转载于:https://www.cnblogs.com/Terry-Wu/p/8343289.html

SpringBoot实战 之 异常处理篇相关推荐

  1. 视频教程-SpringBoot实战视频教程-Java

    SpringBoot实战视频教程 书籍<分布式中间件技术实战 Java版>的作者,拥有6年Java后端开发经验和2年项目管理经验,熟悉Java领域流行技术,拥有多个微服务.分布式项目实战. ...

  2. SpringBoot实战历程第一阶段-钟林森-专题视频课程

    SpringBoot实战历程第一阶段-209人已学习 课程介绍         本课程以实际业务场景为出发点.实战撸码为主.理论概念为辅,零基础一步一个脚印讲解当前微服务项目或分布式系统下Spring ...

  3. SpringBoot 实战 (八) | 使用 Spring Data JPA 访问 Mysql 数据库

    微信公众号:一个优秀的废人 如有问题或建议,请后台留言,我会尽力解决你的问题. 前言 如题,今天介绍 Spring Data JPA 的使用. 什么是 Spring Data JPA 在介绍 Spri ...

  4. SpringBoot 实战 (九) | 整合 Mybatis

    微信公众号:一个优秀的废人 如有问题或建议,请后台留言,我会尽力解决你的问题. 前言 如题,今天介绍 SpringBoot 与 Mybatis 的整合以及 Mybatis 的使用,本文通过注解的形式实 ...

  5. Docker入门实战看这篇就够了(最新详细以及踩过的坑)

    Docker入门实战看这篇就够了 前言 初识 是什么 容器与虚拟机 能干什么 去哪玩 安装 先决条件 查看自己的内核 安装所需的软件包(支持devicemapper存储类型) 设置镜像的仓库 设置yu ...

  6. Redis由浅到深层次讲解和springboot实战(服务器层面的搭建部署)

    Redis由深层次讲解到springboot实战 一.Nosql概述 为什么使用Nosql 1.单机Mysql时代 90年代,一个网站的访问量一般不会太大,单个数据库完全够用.随着用户增多,网站出现以 ...

  7. SpringBoot实战实现分布式锁一之重现多线程高并发场景

    实战前言:上篇博文我总体介绍了我这套视频课程:"SpringBoot实战实现分布式锁" 总体涉及的内容,从本篇文章开始,我将开始介绍其中涉及到的相关知识要点,感兴趣的小伙伴可以关注 ...

  8. 都在强力进阶学习 springboot 实战派文档

    不仅如此,本文还对比讲解了多种同类技术的使用和区别,读者可以根据自己的喜好进行选择. 开发模式:本文讲解了历史悠久,但现在依然被广泛使用的分层应用开发模式 MVC(ModelView Controll ...

  9. SpringBoot运维实用篇

    SpringBoot2零基础到项目实战-基础篇 SpringBoot运维实用篇 从此刻开始,咱们就要进入到实用篇的学习了.实用篇是在基础篇的根基之上,补全SpringBoot的知识图谱.比如在基础篇中 ...

最新文章

  1. java 方法查询_java 几种查询方式【转】
  2. php 数据集转换树、递归重组节点信息多维数组(转)
  3. [转]Tomcat启动报错:AnnotationConfigBeanDefinitionParser are only available on JDK 1.5 and higher...
  4. python实现发送免费短信功能
  5. 关于IM Robot的一些资料【转载】
  6. UnityGI3:光照探针
  7. Redis Lua脚本好处、Redis执行Lua的两种方式、Redis缓存Lua脚本、Redis Lua原子性验证、Lua脚本IP限流、Lua脚本自乘
  8. 数据结构 图-关键路径:AOE网络
  9. 一文述说人工智能(AI)发展史,几经沉浮!
  10. SAP 采购订单入库——库存查询
  11. Android开发关于调用摄像头黑屏没反应的问题
  12. 为什么要配置Java环境变量以及Java环境变量的配置
  13. android chrome 地址栏底部,Android Chrome地址栏下移方便单手操作
  14. syslog与rsyslog
  15. 它来了,它来了,船新 MySQL + MyBatis 版学生管理系统来了
  16. win python ide_Win中同时安装python2和python3及SulimeText3的python IDE搭建
  17. BZOJ 1085 骑士精神
  18. DSP28335的SPWM波生成方法
  19. excel如何将内容拆分为多个表格?
  20. 模拟美萍加密狗--Rockey2虚拟狗(五)

热门文章

  1. ASP.NET MVC学习之控制器篇
  2. Linux下搭建yum服务器
  3. 对Spring的一些个人理解
  4. 为什么民航单位免费给查胸部CT+为什么天气预报中有风向
  5. Python笔记-Collection中Iterable、Iterator和Generator的区别
  6. Cannot start compilation:the output path is not specifired for module “xxx“.Specify the output path
  7. @property和@setter和@getter
  8. im2col原理小结
  9. spark中的println失效问题解决
  10. 图解比较李航书上的viterbi算法和dijistra算法