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

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 却不会,这是因为这两种处理方式的流程存在着本质的差别。

  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

SpringBoot实战 之 异常处理篇的更多相关文章

  1. SpringBoot实战之异常处理篇

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

  2. SpringBootTest单元测试实战、SpringBoot测试进阶高级篇之MockMvc讲解

    1.@SpringBootTest单元测试实战 简介:讲解SpringBoot的单元测试 1.引入相关依赖 <!--springboot程序测试依赖,如果是自动创建项目默认添加--> &l ...

  3. SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver

    关于Web应用的全局异常处理,上一篇介绍了ControllerAdvice结合@ExceptionHandler的方式来实现web应用的全局异常管理: 本篇博文则带来另外一种并不常见的使用方式,通过实 ...

  4. SpringBoot系列教程web篇之全局异常处理

    当我们的后端应用出现异常时,通常会将异常状况包装之后再返回给调用方或者前端,在实际的项目中,不可能对每一个地方都做好异常处理,再优雅的代码也可能抛出异常,那么在 Spring 项目中,可以怎样优雅的处 ...

  5. Spring Cloud实战 | 第十篇 :Spring Cloud + Seata 1.4.1 + Nacos1.4.0 整合实现微服务架构中逃不掉的话题分布式事务

    Seata分布式事务在线体验地址:https://www.youlai.store 本篇完整源码地址:https://github.com/hxrui/youlai-mall 有想加入开源项目开发的童 ...

  6. SpringBoot实战(四)获取接口请求中的参数(@PathVariable,@RequestParam,@RequestBody)

    上一篇SpringBoot实战(二)Restful风格API接口中写了一个控制器,获取了前端请求的参数,现在我们就参数的获取与校验做一个介绍: 一:获取参数 SpringBoot提供的获取参数注解包括 ...

  7. SpringBoot实战(二)Restful风格API接口

    在上一篇SpringBoot实战(一)HelloWorld的基础上,编写一个Restful风格的API接口: 1.根据MVC原则,创建一个简单的目录结构,包括controller和entity,分别创 ...

  8. SpringBoot系列教程web篇之过滤器Filter使用指南

    web三大组件之一Filter,可以说是很多小伙伴学习java web时最早接触的知识点了,然而学得早不代表就用得多.基本上,如果不是让你从0到1写一个web应用(或者说即便从0到1写一个web应用) ...

  9. SpringBoot系列教程web篇之404、500异常页面配置

    接着前面几篇web处理请求的博文,本文将说明,当出现异常的场景下,如404请求url不存在,,403无权,500服务器异常时,我们可以如何处理 原文友链: SpringBoot系列教程web篇之404 ...

随机推荐

  1. Maven初步

    Maven初入 maven 是一个项目管理工具, 它包含了一个 项目对象模型(Project Object Model POM), 一组标准集合, 一个项目生命周期(Project Lifecycle ...

  2. Linux tar 解压的时候报错

    报错如下: # tar zxvf php-7.1.6.tar.gz gzip: stdin: not in gzip format tar: Child returned status 1 tar: ...

  3. Request和Response

    1 简介 web服务器收到客户端的http请求,会针对每一个请求,分别创建一个用于代表请求的request对象和代表响应的response对象. request和response对象既然代表请求和响应 ...

  4. 07_SSH免登录配置

    占位占位占位占位占位占位占位占位

  5. ABP官方文档翻译 4.4 授权

    授权 介绍 关于IPermissionChecker 定义权限 检查权限 使用AbpAuthorize特性 AbpAuthorize特性注意点 抑制授权 使用IPermissionChecker 在R ...

  6. win7 重装 docker 启动后无法启动错误解决

    描述 win7 重新安装Docker 后启动  Docker Quickstart Terminal 出现如下错误 Starting "default"... (default) ...

  7. 《Thinking in Java》学习笔记(四)

    1.Java中的闭包与回调 闭包(Closure)是一种能被调用的对象,它保存了创建它的作用域的信息.JAVA并不能显式地支持闭包,但是在JAVA中,闭包可以通过“接口+内部类”来实现,因为对于非静态 ...

  8. webrtc初探之一对一的连接过程(一)

    说明,我研究的是muan-khan的一个github项目,针对的是chrome对chrome,也就是pc对pc的一对一,一对多通话,感兴趣的可以继续往下看. github地址:https://gith ...

  9. Java流机制学习

    基本概念 BaseStream 基础流是一个可行并行或者串行的汇聚操作的元素序列.可以进行顺序遍历,也可以进行并发遍历.通过它也可以得到一个并行流或者串行流. Stream 是Java中流的表现接口, ...

  10. 【原创】 c#单文件绿色资源自更新

    先引用dnlib.dll 更新程序先fody成一个文件 放置主程序资源文件 更新程序.exe #region using System; using System.Diagnostics; using ...