在互联网时代,咱们所开发的应用大可能是直面用户的,程序中的任何一点小疏忽均可能致使用户的流失,而程序出现异常每每又是不可避免的,那该如何减小程序异常对用户体验的影响呢?其实方法很简单,对异常进行捕获,而后给予相应的处理便可。但实现的方式却有好多种,例如:前端
try { ... } catch (Exception e) { doSomeThing(); }
像这种标准的 try-catch 是能够解决问题,但若是让你在每一个接口实现里面都 try-catch 一下,我想你应该是不太愿意的。那么下面来介绍下 SpringBoot 为咱们提供的处理方式。java
首先,咱们来模拟一下,出现异常的场景,方式比较简单,直接在正常的代码里面抛出一个异常便可。git
在上面的示例中,调用接口时,出现了异常,但客户端却收到一个相对正常的响应,这是由于 SpringBoot 默认提供了一个 /error 的映射,该映射被注册为 Servlet 容器中的一个全局错误页面用来合理处理全部的异常状况。但示例中的响应报文不符合咱们定义的数据规范,想要使其知足本身的数据规范,能够本身定义一个新的 ErrorController,代码以下:github
@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 }
熟悉 SpringMVC 的人应该都知道 @ExceptionHandler 这个注解,在 SpringBoot 里面,咱们一样可使用它来作异常捕获。app
这种方式使用场景较少,但做为学习 @ExceptionHandler 入门示例仍是很是不错的,直接在对应的 Controller 里面增长一个异常处理的方法,并使用 @ExceptionHandler 标识它便可。ide
@ExceptionHandler(Exception.class) public Result handleException() { return new Result(ResultCode.WEAK_NET_WORK); }
客户端获得的效果与使用 ErrorController 彻底一致,但对于服务端来讲却不太同样,若是仔细观察这两种方式的日志输出的话,会发现使用 ErrorController 时,后台会打印出异常堆栈信息,而使用 @ExceptionHandler 却不会,这是由于这两种处理方式的流程存在着本质的差异。学习
ErrorController: 调用 UserController 抛出异常时,自身没有作任何处理,因此会打印出堆栈信息,但这个异常会被 Servlet 容器捕捉到,Servlet 容器再将请求转发给注册好的异常处理映射 /error 作处理,客户端收到的实际是 ErrorController 的处理结果,而不是 UserController 的。ui
ExceptionHandler: 异常的处理方法直接被定义在 UserController 里面,也就是说,在异常抛出的时候,UserController 会使用本身的方法去作异常处理,而不会抛出给 Servlet 容器,因此这个地方没有打印堆栈信息。.net
若是想要在后台添加堆栈信息的输出也很是简单,只须要将该异常做为一个参数传递给异常处理方法,而后在处理方法里面作相应的操做便可。
@ExceptionHandler(Exception.class) public Result handleException(Exception e) { e.printStackTrace(); return new Result(ResultCode.WEAK_NET_WORK); }
项目的每每存在着多个 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 { ... }
对于使用父级 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); } }
实际的开发场景中,异常是区分不少类别的,不一样类别的异常须要给用户不一样的反馈。例如,在 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