异样是每一个利用必须要解决的问题。
Spring MVC我的项目,如果不做任何的异样解决的话,产生异样后,异样堆栈信息会间接抛出到页面。
比方,咱们在Controller写一个异样:
@GetMapping(value="/hello",produces={"text/html; charset=UTF-8"}) @ResponseBody public String hello(ModelAndView model){ int c = 100 / 0; return "<h1>User info</h1>"; }
浏览器拜访:
用户体验相当不好,所以个别状况下,利用必须要想方法解决异样。
异样解决形式
常见的异样解决形式,无非:
- 利用中对所有可能产生异样的中央,都try catch,捕捉异样后做相应的解决。
- 集中处理异样。
第一种形式显然不好,一方面是代码中须要到处都写try catch,万一某一段代码因为程序员的忽略没有写,异样就会抛出到前台,很不好。
另外,某些状况下异样是不能捕捉的,比方须要事务处理的代码,捕捉异样后会影响到事务回滚。
所以,咱们还是须要想方法用第2中形式来解决异样。n多年前已经做过一个摩托罗拉的我的项目,其中一项需要就是对一个线上零碎的异样做解决、不容许异样信息抛出到前台页面。当初那个我的项目并没有采纳相似SpringMVC的框架,所以最终提出了用filter、在filter中拦挡异样的解决计划并且被客户驳回。
其实SpringMVC的对立异样解决计划和我下面我的项目中采纳的计划十分相似。
SpringMVC的异样解决
Spring MVC提供了如下异样解决选项:
- @ExceptionHandle
- @ExceptionHandle + @ControllerAdvice
- 自定义异样处理器HandlerExceptionResolver
@ExceptionHandle
@ExceptionHandle能够作用在Controller中,比方,在下面产生异样的Controller中减少一个@ExceptionHandle注解的办法:
@GetMapping(value="/hello",produces={"text/html; charset=UTF-8"}) @ResponseBody public String hello(ModelAndView model){ int c = 100 / 0; return "<h1>User info</h1>"; } @ExceptionHandler(Exception.class) @ResponseBody public String handle(Exception ex){ return "错了在helloworld Controller error msg is ==="; }
运行:
阐明Hello办法中产生的异样,曾经被handle办法解决,前台页面不再会出现异常信息。
问题解决了!
然而@ExceptionHandle只在以后Controller文件中失效,也就是说,以后Controller中的办法、或者办法调用的service层、dao层等产生的异样,才会被捕捉到。其余Controller中产生的异样仍然不会被捕捉。
这样的话,就须要对每一个Controller减少@ExceptionHandle进行解决,解决起来还是有点麻烦。
对立异样解决
@ExceptionHandle + @ControllerAdvice能够实现对立异样解决。
@ControllerAdvice注解能够实现:
On startup, RequestMappingHandlerMapping and ExceptionHandlerExceptionResolver detect controller advice beans and apply them at runtime. Global @ExceptionHandler methods, from an @ControllerAdvice, are applied after local ones, from the @Controller. By contrast, global @ModelAttribute and @InitBinder methods are applied before local ones.
SpringMVC启动的过程中,RequestMappingHandlerMapping和ExceptionHandlerExceptionResolver会检测到advice bean并且在运行时会应用他们。在@ControllerAdvice中的@ExceptionHandler会变成一个全局的异样处理器、在本地异样处理器之后失效。并且,@ModelAttribute和 @InitBinder会在本地的之后失效。
这段话的意思就是,@ExceptionHandle + @ControllerAdvice之后,@ExceptionHandle就会变成全局异样处理器。所谓的本地异样处理器,就是写在Controller中的@ExceptionHandle异样处理器。
这个工作是ExceptionHandlerExceptionResolver干的,源码中能够看到:
@Nullable protected ServletInvocableHandlerMethod getExceptionHandlerMethod( @Nullable HandlerMethod handlerMethod, Exception exception) { Class<?> handlerType = null; //先找"local"异样处理器 if (handlerMethod != null) { // Local exception handler methods on the controller class itself. // To be invoked through the proxy, even in case of an interface-based proxy. handlerType = handlerMethod.getBeanType(); ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType); if (resolver == null) { resolver = new ExceptionHandlerMethodResolver(handlerType); this.exceptionHandlerCache.put(handlerType, resolver); } Method method = resolver.resolveMethod(exception); if (method != null) { return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method); } // For advice applicability check below (involving base packages, assignable types // and annotation presence), use target class instead of interface-based proxy. if (Proxy.isProxyClass(handlerType)) { handlerType = AopUtils.getTargetClass(handlerMethod.getBean()); } } //再找advice的异样处理器 for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) { ControllerAdviceBean advice = entry.getKey(); if (advice.isApplicableToBeanType(handlerType)) { ExceptionHandlerMethodResolver resolver = entry.getValue(); Method method = resolver.resolveMethod(exception); if (method != null) { return new ServletInvocableHandlerMethod(advice.resolveBean(), method); } } } return null; }
验证一下。
加一个MyGlobalExceptionController:
@ControllerAdvicepublic class MyGlobalExceptionController { @ExceptionHandler(Exception.class) @ResponseBody public String handle(Exception ex){ return return "错了MyGlobalExceptionController error msg is ==="; }}
先不去掉HelloWorldController中的异样处理器,运行hello:
失效的是HelloWorldController中的异样处理器。
而后去掉HelloWorldController中的异样处理器:
写在MyGlobalExceptionController中的全局异样处理器失效!
自定义异样处理器HandlerExceptionResolver
实现接口HandlerExceptionResolver,或者扩大虚构类AbstractHandlerMethodExceptionResolver(勉强能够思考),定义本人的异样处理器。
其实,非必要(没想到有什么必要性)不倡议!
上一篇 Spring MVC 九:Context层级(基于配置)