一、统一异常处理的优点前端
在开发中,咱们是否遇到过以下两种奇葩现象:java
(1)只要没有成功,无论什么缘由,前端界面给出提示:服务端错误/异常。哪怕是数据校验不过,也这样提示(嗯,反正先把锅甩出去再说,具体什么缘由我才不在意呢,老子就是这么聪明);spring
若是想学习Java工程化、高性能及分布式、深刻浅出。微服务、Spring,MyBatis,Netty源码分析的朋友能够加个人Java高级交流:787707172,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给你们。后端
(2)前端不作任何提示,一切提示信息都来自后端,成功的时候天然没什么,失败的时候,好比将Exception的描述信息(e.getMessage)返回。springboot
现象(1)没什么好说的,直接拖出去枪毙吧;现象(2)先把产品经理宰了再说吧,看起来好像很专业的样子,出了什么问题直接看response返回的结果就知道个大概,研发测试都很方便,只是,你们想过没有,研发测试运维的问题,凭什么要用户买单,你见过淘宝京东有时候出了问题给你相似于“out of memory”的异常提示吗?架构
那么异常统一处理有什么好处呢?mvc
提升用户体验;app
业务逻辑和异常处理逻辑解耦;运维
对异常进行分类统一处理,减小冗余代码;分布式
便于代码风格统一,而且更优雅(好比参数校验的时候,得写不少if else,而且不一样的人写法不一致);
二、统一异常处理的实现
2.1 springboot的默认异常处理
Spring Boot提供了一个默认的映射:/error,当处理中抛出异常以后,会转到该请求中处理,而且该请求有一个全局的错误页面用来展现异常内容。
好比:
@RestController public class Test { @RequestMapping(value = {"/test"},method = RequestMethod.GET) public String test(@RequestParam("id")Integer id){ return "id:"+id; } }
运行后访问结果以下:
这种直接返回错误页面,对于用户而言,显然是太不友好了哈!
2.2 统一异常处理
java异常详解
首先,定义本身的异常类,随便起个名字哈,MyException.java
public class MyException extends Exception{ private Integer code; private String Message; public MyException(Integer code,String Message) { this.code = code; this.Message = Message; } }
而后定义本身的异常处理类,ExceptionHandle.java
若是想学习Java工程化、高性能及分布式、深刻浅出。微服务、Spring,MyBatis,Netty源码分析的朋友能够加个人Java高级交流:787707172,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给你们。
若是返回的对象是JSON的话,能够用@RestControllerAdvice
@ControllerAdvice
public class ExceptionHandle { private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class); @ExceptionHandler(value = Exception.class) @ResponseBody public Result handle(Exception e) { if (e instanceof MyException) { MyException myException = (MyException) e; return ResultUtil.error(boyException.getCode(), boyException.getMessage()); }else { logger.error("【系统异常】{}", e); return new Result(-1, "未知错误"); } } }
三、统一异常处理源码解析
3.1 注解源码解析
java注解详解
@ControllerAdvice
@ExceptionHandler
@RestControllerAdvice与@ExceptionHandler注解是sprngmvc中与异常捕获与处理相关的注解,它的入口也是DispatcherServlet中的doDispatcher()方法中,以下:
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
后面会进入HandlerExceptionResolverComposite的resolveException方法,这个ExceptionHandlerResolverComposite包含三个ExcpetionHandlerResolver,是在springmvc中生成的,在springboot中其生成代码以下:
@Bean public HandlerExceptionResolver handlerExceptionResolver() { List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>(); configureHandlerExceptionResolvers(exceptionResolvers); if (exceptionResolvers.isEmpty()) { addDefaultHandlerExceptionResolvers(exceptionResolvers); } extendHandlerExceptionResolvers(exceptionResolvers); HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite(); composite.setOrder(0); composite.setExceptionResolvers(exceptionResolvers); return composite; }
后面他会进入ExceptionHandlerExceptionResolver类的方法:
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) { ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception); }
在这个方法中的第一行,getExceptionHandlerMethod方法,其进行了查找对应的带有@ControllerAdvice注解的类型和对应匹配的方法,而后在doResolverHandlerMethod方法中进行了处理,这就是整个流程。
@ControllerAdvice的加载过程:
首先在springboot扫描的时候,会把@ControllerAdvice的bean放入到beanFactory里面去,此时只要从beanFactory中获取到须要的bean便可,处理方式在ExceptionHandlerExceptionResolver类中:
@Override public void afterPropertiesSet() { // Do this first, it may add ResponseBodyAdvice beans initExceptionHandlerAdviceCache(); private void initExceptionHandlerAdviceCache() { if (getApplicationContext() == null) { return; } if (logger.isDebugEnabled()) { logger.debug("Looking for exception mappings: " + getApplicationContext()); } List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
封装好后,获取带有@Exceptionhandler的注解方法,即根据异常类型进行调用了。
欢迎工做一到八年的Java工程师朋友们加入Java高级交流:787707172
本群提供免费的学习指导 架构资料 以及免费的解答
不懂得问题均可以在本群提出来 以后还会有直播平台和讲师直接交流噢