个人学习笔记分享,当前能力有限,请勿贬低,菜鸟互学,大佬绕道
如有勘误,欢迎指出和讨论,本文后期也会进行修正和补充
前言
在未进行全局捕获的情况下,异常有两种处理结果
- 主动try...catch捕获,对每种情况进行针对性处理。缺点如下:
- 代码冗长,需要考虑各种各样的情况,无端增加工作量;
- 人非完人,不可能考虑完整所有情况,有部分情况甚至无从捕获
- 被动处理,不捕获,异常信息最终将会被直接打印到前端。缺点如下:
- 正常用户根本看不懂异常信息,大量的异常信息对于用户而言跟乱码无异
- 异常信息会打印出后端的信息,泄露后端数据,造成巨大安全隐患,尤其是sql错误会打印出sql语句,直接暴露表名和字段名
- 前端仅能判断执行成功与否,而无法根据具体情况作出处理,降低了项目的健壮性
而进行全局异常捕获后,则可在控制器层面对所有异常进行处理,不同异常返回不同信息,与前端约定好数据格式后返回统一格式的数据。
因而,全局异常已经成为项目必要的组件之一。
1.介绍
通常借助注解@ControllerAdvice
和@ExceptionHandler
完成,当有异常被抛至控制层时,便可对其进行统一处理,返回约定好的json格式,或者某个页面
2.集成
-
编写好controller层相关代码,此处不做赘述
-
创建Controller增强器
@Slf4j @ControllerAdvice public class BaseExceptionHandler { }
- @Slf4j:标记使用
Slf4j
日志框架,此处不做赘述 - @ControllerAdvice:标记当前类为controller蹭强器
- @Slf4j:标记使用
-
创建异常拦截方法,可创建多个
-
拦截返回mav对象方法
@ExceptionHandler({NestedServletException.class}) @ResponseStatus(HttpStatus.OK) @ResponseBody public ModelAndView servletException(HttpServletRequest request, HttpServletResponse response, Exception exception) throws ServletException, IOException { ModelAndView mav = new ModelAndView(); ReturnMsg message = ReturnMsg.FAIL; out(response, message); return mav; }
- @ExceptionHandler:参数为拦截的异常类型,可枚举多种类型,但整个controller蹭强器下,一个异常仅被拦截处理一次(按照先后顺序)
- @ResponseStatus:响应状态,通常用
HttpStatus.OK
即可 - @ResponseBody:返回json对象
-
拦截返回自定义对象方法
@ExceptionHandler({Exception.class}) @ResponseStatus(HttpStatus.OK) @ResponseBody public ReturnMsg processException(HttpServletRequest request, HttpServletResponse response, Exception exception) throws ServletException, IOException { // 打印异常信息至控制台,开始处理异常 log.error("异常统一处理-Exception:" + exception.getLocalizedMessage(), exception); //异常默认为是操作失败 ReturnMsg message = ReturnMsg.FAIL; // 检查异常的类型 if (exception instanceof NestedServletException) { // 异步请求错误,已处理 } else if (exception instanceof BaseException) { // 自定义类型的异常,转换为自定义异常 message = ((BaseException) exception).asReturnMsg(); } else { // 非自定义类型异常,打印错误信息至日志,封装ReturnMsg对象 log.error(request.getRequestURI(), exception); message = ReturnMsg.FAIL; } //返回消息体 return message; }
-
数据写入response方法
public static void out(ServletResponse response, ReturnMsg returnMsg) { PrintWriter out = null; try { response.setContentType("application/json;charset=utf-8"); out = response.getWriter(); out.println(ObjectMapperFactory.getInstance().writeValueAsString(returnMsg)); } catch (Exception e) { e.printStackTrace(); } finally { if (null != out) { out.flush(); out.close(); } } }
-
3.使用
- 需要处理的异常,可以try...catch捕获后处理,也可以直接抛出自定义异常,如
throw new BaseException("密码错误")
,由controller蹭强器转换为约定的格式后返回前端 - 无需处理的异常,就不管咯,controller蹭强器处理后返回前端信息,但请自行完善增强器,以处理各种不同情况
- 异常会被打印到控制台,并移交给日志框架处理,用于线上版本查看日志
4.相关资料
- 自定义异常及统一数据返回格式:请自行查阅相关资料,实现方案很简单易懂,本人也有做相关笔记
5.补充
- 请慎用Preconditions及其类似工具检查数据,数据违规时抛出的异常将会和其余运行异常混淆,无法区分,要么放弃此类工具,要么自行寻找解决方案
- 尽管全局处理了,但异常就是异常,是不正常的情况。前端数据异常请返回数据告知前端,后端问题请尽可能完善代码以规避。异常就是异常,处理了仍然是异常
BB两句
这类框架性组件尽早定下来,当前项目已经大范围使用了Preconditions。。。花了半天没有完美解决方案,只能做出让步进行兼容处理,处女座表示相当难受
作者:Echo_Ye
WX:Echo_YeZ
email :echo_yezi@qq.com
个人站点:在搭了在搭了。。。(右键 - 新建文件夹)