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 {@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 却不会,这是因为这两种处理方式的流程存在着本质的差别。
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
原文链接:http://blog.csdn.net/chinrui/article/details/71036544
转载于:https://www.cnblogs.com/Terry-Wu/p/8343289.html
SpringBoot实战 之 异常处理篇相关推荐
- 视频教程-SpringBoot实战视频教程-Java
SpringBoot实战视频教程 书籍<分布式中间件技术实战 Java版>的作者,拥有6年Java后端开发经验和2年项目管理经验,熟悉Java领域流行技术,拥有多个微服务.分布式项目实战. ...
- SpringBoot实战历程第一阶段-钟林森-专题视频课程
SpringBoot实战历程第一阶段-209人已学习 课程介绍 本课程以实际业务场景为出发点.实战撸码为主.理论概念为辅,零基础一步一个脚印讲解当前微服务项目或分布式系统下Spring ...
- SpringBoot 实战 (八) | 使用 Spring Data JPA 访问 Mysql 数据库
微信公众号:一个优秀的废人 如有问题或建议,请后台留言,我会尽力解决你的问题. 前言 如题,今天介绍 Spring Data JPA 的使用. 什么是 Spring Data JPA 在介绍 Spri ...
- SpringBoot 实战 (九) | 整合 Mybatis
微信公众号:一个优秀的废人 如有问题或建议,请后台留言,我会尽力解决你的问题. 前言 如题,今天介绍 SpringBoot 与 Mybatis 的整合以及 Mybatis 的使用,本文通过注解的形式实 ...
- Docker入门实战看这篇就够了(最新详细以及踩过的坑)
Docker入门实战看这篇就够了 前言 初识 是什么 容器与虚拟机 能干什么 去哪玩 安装 先决条件 查看自己的内核 安装所需的软件包(支持devicemapper存储类型) 设置镜像的仓库 设置yu ...
- Redis由浅到深层次讲解和springboot实战(服务器层面的搭建部署)
Redis由深层次讲解到springboot实战 一.Nosql概述 为什么使用Nosql 1.单机Mysql时代 90年代,一个网站的访问量一般不会太大,单个数据库完全够用.随着用户增多,网站出现以 ...
- SpringBoot实战实现分布式锁一之重现多线程高并发场景
实战前言:上篇博文我总体介绍了我这套视频课程:"SpringBoot实战实现分布式锁" 总体涉及的内容,从本篇文章开始,我将开始介绍其中涉及到的相关知识要点,感兴趣的小伙伴可以关注 ...
- 都在强力进阶学习 springboot 实战派文档
不仅如此,本文还对比讲解了多种同类技术的使用和区别,读者可以根据自己的喜好进行选择. 开发模式:本文讲解了历史悠久,但现在依然被广泛使用的分层应用开发模式 MVC(ModelView Controll ...
- SpringBoot运维实用篇
SpringBoot2零基础到项目实战-基础篇 SpringBoot运维实用篇 从此刻开始,咱们就要进入到实用篇的学习了.实用篇是在基础篇的根基之上,补全SpringBoot的知识图谱.比如在基础篇中 ...
最新文章
- java 方法查询_java 几种查询方式【转】
- php 数据集转换树、递归重组节点信息多维数组(转)
- [转]Tomcat启动报错:AnnotationConfigBeanDefinitionParser are only available on JDK 1.5 and higher...
- python实现发送免费短信功能
- 关于IM Robot的一些资料【转载】
- UnityGI3:光照探针
- Redis Lua脚本好处、Redis执行Lua的两种方式、Redis缓存Lua脚本、Redis Lua原子性验证、Lua脚本IP限流、Lua脚本自乘
- 数据结构 图-关键路径:AOE网络
- 一文述说人工智能(AI)发展史,几经沉浮!
- SAP 采购订单入库——库存查询
- Android开发关于调用摄像头黑屏没反应的问题
- 为什么要配置Java环境变量以及Java环境变量的配置
- android chrome 地址栏底部,Android Chrome地址栏下移方便单手操作
- syslog与rsyslog
- 它来了,它来了,船新 MySQL + MyBatis 版学生管理系统来了
- win python ide_Win中同时安装python2和python3及SulimeText3的python IDE搭建
- BZOJ 1085 骑士精神
- DSP28335的SPWM波生成方法
- excel如何将内容拆分为多个表格?
- 模拟美萍加密狗--Rockey2虚拟狗(五)
热门文章
- ASP.NET MVC学习之控制器篇
- Linux下搭建yum服务器
- 对Spring的一些个人理解
- 为什么民航单位免费给查胸部CT+为什么天气预报中有风向
- Python笔记-Collection中Iterable、Iterator和Generator的区别
- Cannot start compilation:the output path is not specifired for module “xxx“.Specify the output path
- @property和@setter和@getter
- im2col原理小结
- spark中的println失效问题解决
- 图解比较李航书上的viterbi算法和dijistra算法