SpringBoot实战之异常处理篇
在互联网时代,我们所开发的应用大多是直面用户的,程序中的任何一点小疏忽都可能导致用户的流失,而程序出现异常往往又是不可避免的,那该如何减少程序异常对用户体验的影响呢?其实方法很简单,对异常进行捕获,然后给予相应的处理即可。但实现的方式却有好多种,例如:
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 {
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping
@ResponseBody
public 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 却不会,这是因为这两种处理方式的流程存在着本质的差别。
ErrorController: 调用 UserController 抛出异常时,自身没有做任何处理,所以会打印出堆栈信息,但这个异常会被 Servlet 容器捕捉到,Servlet 容器再将请求转发给注册好的异常处理映射 /error 做处理,客户端收到的实际是 ErrorController 的处理结果,而不是 UserController 的。
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
SpringBoot实战之异常处理篇的更多相关文章
- SpringBoot实战 之 异常处理篇
在互联网时代,我们所开发的应用大多是直面用户的,程序中的任何一点小疏忽都可能导致用户的流失,而程序出现异常往往又是不可避免的,那该如何减少程序异常对用户体验的影响呢?其实方法很简单,对异常进行捕获,然 ...
- SpringBootTest单元测试实战、SpringBoot测试进阶高级篇之MockMvc讲解
1.@SpringBootTest单元测试实战 简介:讲解SpringBoot的单元测试 1.引入相关依赖 <!--springboot程序测试依赖,如果是自动创建项目默认添加--> &l ...
- SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver
关于Web应用的全局异常处理,上一篇介绍了ControllerAdvice结合@ExceptionHandler的方式来实现web应用的全局异常管理: 本篇博文则带来另外一种并不常见的使用方式,通过实 ...
- SpringBoot系列教程web篇之全局异常处理
当我们的后端应用出现异常时,通常会将异常状况包装之后再返回给调用方或者前端,在实际的项目中,不可能对每一个地方都做好异常处理,再优雅的代码也可能抛出异常,那么在 Spring 项目中,可以怎样优雅的处 ...
- Spring Cloud实战 | 第十篇 :Spring Cloud + Seata 1.4.1 + Nacos1.4.0 整合实现微服务架构中逃不掉的话题分布式事务
Seata分布式事务在线体验地址:https://www.youlai.store 本篇完整源码地址:https://github.com/hxrui/youlai-mall 有想加入开源项目开发的童 ...
- SpringBoot实战(四)获取接口请求中的参数(@PathVariable,@RequestParam,@RequestBody)
上一篇SpringBoot实战(二)Restful风格API接口中写了一个控制器,获取了前端请求的参数,现在我们就参数的获取与校验做一个介绍: 一:获取参数 SpringBoot提供的获取参数注解包括 ...
- SpringBoot实战(二)Restful风格API接口
在上一篇SpringBoot实战(一)HelloWorld的基础上,编写一个Restful风格的API接口: 1.根据MVC原则,创建一个简单的目录结构,包括controller和entity,分别创 ...
- SpringBoot系列教程web篇之过滤器Filter使用指南
web三大组件之一Filter,可以说是很多小伙伴学习java web时最早接触的知识点了,然而学得早不代表就用得多.基本上,如果不是让你从0到1写一个web应用(或者说即便从0到1写一个web应用) ...
- SpringBoot系列教程web篇之404、500异常页面配置
接着前面几篇web处理请求的博文,本文将说明,当出现异常的场景下,如404请求url不存在,,403无权,500服务器异常时,我们可以如何处理 原文友链: SpringBoot系列教程web篇之404 ...
随机推荐
- 2019-6-14-WPF-shows-that-some-windows-in-multithreading-will-be-locked-in-the-PenThreadWorker-constr...
title author date CreateTime categories WPF shows that some windows in multithreading will be locked ...
- (补充)06.Hibernate持久化类&持久化对象
持久化类:就是一个Java类(JavaBean),这个类与表建立映射关系就可以成为是持久类 持久化类 = JavaBean + xxx.hbm.xml 编写规则: 1.提供一个无参数,public访问 ...
- js实现事件委托
事件委托的概念: 事件委托就是利用事件冒泡,把事件加到父元素或祖先元素上,触发执行效果. 事件委托的写法: btn6.onclick = function(event){ event = event ...
- SQLSERVER 数据库管理员的专用连接DAC
DAC:Dedicated Admin Connection 当SQL Server因系统资源不足,或其它异常导致无法建立数据库连接时, 可以使用系统预留的DAC连接到数据库,进行一些问题诊断和故障排 ...
- LRU Cache数据结构简介
什么是LRU Cache LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法. 什么是Cache?狭义的Cache指的是位于CPU和主存间的快速RAM ...
- Python学习 备注(2)
由于python下许多框架都是在python2下的,python3向下不兼容 需安装python2 安装好后,使用pip安装python下的框架scrapy 总是报错,最后发现是包管理器,pip的版本 ...
- java 中Vector的使用详解
Vector 可实现自动增长的对象数组. java.util.vector提供了向量类(vector)以实现类似动态数组的功能.在Java语言中没有指针的概念,但如果正确灵活地使用指针又确实可以大大提 ...
- 矩阵快速幂求Fibonacci
原理 我们取矩阵A 则 F1=F2=1;则可以轻易求出F(i) #define maxn 2 #define mo 1000000007 struct Matrix{ long long a[maxn ...
- [Array]448. Find All Numbers Disappeared in an Array
Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and ot ...
- 安装py3ditles中遇到的问题
安装时,在"(venv)$ pip install -e ." 会遇到"No module named 'pip._vendor.progress.helpers' ht ...