springmvc异常处理 spring中有三种方式可以优雅的处理异常
使用@ExceptionHandler
使用HandlerExceptionResolver
使用@ControllerAdvice+@ExceptionHandler
使用@ExceptionHandler
该方式只在指定的@Controller有效,不会对其他的@Controller产生影响
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Controller @RequestMapping("/exception") public class ExceptionController { @ExceptionHandler(BusinessException.class) @ResponseBody public String exception (Exception e) { return "出现异常" +e.getMessage(); } @RequestMapping("/testException") @ResponseBody public String testException () { User user = null ; System.out.println(user.getId()); return "success" ; } @RequestMapping("/testBusinessException") @ResponseBody public String testBusinessException () { throw new BusinessException(); } }
此时如果访问/exception/testBusinessException出现异常,就会跳转到exception方法中,将结果返回给浏览器
由于该方式只对@ExceptionHandler注解指定方法所在的Controller中生效,所以为了可以针对多个Controller生效,需要将@ExceptionHandler注解指定方法抽离到一个Controller基类中
1 2 3 4 5 6 7 8 @Controller public class BaseController { @ExceptionHandler(BusinessException.class) @ResponseBody public String exception (Exception e) { return "出现异常" +e.getMessage(); } }
然后需要该异常处理的Controller继承该基类
1 2 3 4 5 6 7 8 9 10 @Controller @RequestMapping("/exception") public class ExceptonController1 extends BaseController { @RequestMapping("/testBusinessException1") @ResponseBody public String testBusinessException () { throw new BusinessException(); } }
使用HandlerExceptionResolver 处理器异常解析器接口是负责处理各类控制器执行过程中出现的异常
1 2 3 4 5 6 public interface HandlerExceptionResolver { ModelAndView resolveException ( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) ;}
该方式是在DispatcherServlet中默认使用的,在processHandlerException()方法中,调用异常解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 protected ModelAndView processHandlerException (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { ModelAndView exMv = null ; for (HandlerExceptionResolver handlerExceptionResolver : this .handlerExceptionResolvers) { exMv = handlerExceptionResolver.resolveException(request, response, handler, ex); if (exMv != null ) { break ; } } if (exMv != null ) { if (exMv.isEmpty()) { request.setAttribute(EXCEPTION_ATTRIBUTE, ex); return null ; } if (!exMv.hasView()) { exMv.setViewName(getDefaultViewName(request)); } if (logger.isDebugEnabled()) { logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex); } WebUtils.exposeErrorRequestAttributes(request, ex, getServletName()); return exMv; } throw ex; }
其有四个实现类
DefaultHandlerExceptionResolver DefaultHandlerExceptionResolver在DispatcherServlet中是默认使用的,用于将Spring中的标准异常解析为对应的HTTP状态码,但是响应体并不会改变
ResponseStatusExceptionResolver ResponseStatusExceptionResolver在DispatcherServlet中是默认使用的,主要是和自定义异常上配置的@ResponseStatus注解进行搭配使用,将自定义异常映射到设定的HTTP状态码,与DefaultHandlerExceptionResolver一样,只是更改了状态码,并没有改变响应体
该异常处理机制是来解析@ResponseStatus来标注的异常
自定义异常
1 2 3 4 5 @ResponseStatus(code = HttpStatus.BAD_REQUEST,reason = "出现业务异常") public class BusinessException extends RuntimeException {}
1 2 3 4 5 @RequestMapping("/testBusinessException") @ResponseBody public String testBusinessException () { throw new BusinessException(); }
调用该接口就会返回到状态码为400的错误页面
SimpleMappingExceptionResolver SimpleMappingExceptionResolver用来映射异常类名到视图名
AnnotationMethodHandlerExceptionResolver AnnotationMethodHandlerExceptionResolver通过注解@ExceptionHandler来处理异常,已经被废弃
ExceptionHandlerExceptionResolver
如果出现异常,先是查找该Controller中用@ExceptionHandler注解定义的方法
如果没有找到@ExceptionHandler注解的话,就会寻找标记了@ControllerAdvice注解的类中的@ExceptionHandler注解方法
除此之外,还可以自定义异常解析器,继承AbstractHandlerMethodExceptionResolver
自定义异常解析器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Component public class CustomExceptionResolver extends AbstractHandlerExceptionResolver { @Override protected ModelAndView doResolveException (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { if (ex instanceof BusinessException){ System.out.println("出现业务异常" ); ModelAndView modelAndView = new ModelAndView(); modelAndView.setStatus(HttpStatus.BAD_REQUEST); modelAndView.addObject("msg" ,"出现业务异常" ); return modelAndView; } return null ; } }
但是由于该自定义解析依然返回的是ModelAndView,所以与目前前后端分离的项目不太搭
使用@ControllerAdvice+@ExceptionHandler 在前面的ExceptionHandlerExceptionResolver中已经用到了@ControllerAdvice,其实@ControllerAdvice是对于@ExceptionHandler的一个补充,使得可以进行全局的异常解析,可以将之前多个分散的@ExceptionHandler整合起来,合并成为一个单一的全局的异常处理中
1 2 3 4 5 6 7 8 9 @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler @ResponseBody public String exception (Exception e) { return "全局捕获: 出现异常" +e.getMessage(); } }
它允许对响应体和HTTP状态码进行完全控制
它允许将几个异常映射到相同的方法,以便一起处理
它充分利用了新的REST风格的 ResposeEntity响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface ControllerAdvice { @AliasFor("basePackages") String[] value() default {}; @AliasFor("value") String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] assignableTypes() default {}; Class<? extends Annotation>[] annotations() default {}; }
局部异常处理 在当前Controller中处理异常(当前Controller中使用@ExceptionHandler标注的方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Controller @RequestMapping("/exception") public class ExceptionController { @ExceptionHandler @ResponseBody public String exception (Exception e) { return "出现异常" +e.getMessage(); } @RequestMapping("/testException") @ResponseBody public String testException () { User user = null ; System.out.println(user.getId()); return "success" ; } }
全局异常处理 如果当前Controller中没有异常处理,则会使用全局异常(使用@ControllerAdvice标注的类中的@ExceptionHandler方法)
1 2 3 4 5 6 7 8 9 @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler @ResponseBody public String exception (Exception e) { return "全局捕获: 出现异常" +e.getMessage(); } }
也可以使用@RestControllerAdvice,相当于@ControllerAdvice+@ResponseBody
Tips @ControllerAdvice注解可搭配的方式很多
@ControllerAdvice+@ExceptionHandler可以实现全局异常处理
@ControllerAdvice+@ModelAttribute可以实现全局数据绑定
@ControllerAdvice+@InitBinder可以实现全局数据预处理