上面分析了Spring默认的异常处理实现类DefaultHandlerExceptionResolver.它处理的异常是Spring预定义的几种常见异常, 它将异常对应到HTTP的状态码. 而对于不属于这些类型的其他异常, 我们可以使用ResponseStatusExceptionResolver来处理, 将其对应到HTTP状态码.
三. ResponseStatusExceptionResolver
如何使用?
@GetMapping("/responseStatus") @ResponseBody public String responseStatus() throws MyException { throw new MyException(); } @ResponseStatus(code = HttpStatus.BAD_GATEWAY) public class MyException extends Exception{}只需要在异常上使用@ResponseStatus注解即可将特定的自定义异常对应到Http的状态码.
四. ExceptionHandlerExceptionResolver
使用类似于普通的controller方法, 使用@ExceptionHandler注解的方法将作为处理该注解参数中异常的handler. 比如, 在一个controller中, 我们定义一个处理NPE的异常处理handler方法, 可以用来处理该controller中抛出的NPE. 代码如下:
@GetMapping("/npe1") @ResponseBody public String npe1() throws NullPointerException { throw new NullPointerException(); } @GetMapping("/npe2") @ResponseBody public String npe2() throws NullPointerException { throw new NullPointerException(); } @ExceptionHandler(value = {NullPointerException.class}) @ResponseBody public String npehandler(){ return "test npe handler"; }无论是请求/npe1还是请求/npe2, 系统都会抛出异常, 并交给对应的处理程序npehandler去处理. 使用@ExceptionHandler(value = {NullPointerException.class})注解的方法可以处理本controller范围内的所有方法排除的npe异常, 如果要将其作为应用中所有controller的异常处理器, 就要将其定义在@ControllerAdvice注解的类中.
@ControllerAdvice public class ControllerAdvicer { @ExceptionHandler(value = {NullPointerException.class}) @ResponseBody public String npehandler(){ return "test npe handler in advice"; } }要了解其原理, 需要查看ExceptionHandlerExceptionResolver中的方法doResolveHandlerMethodException
@Override @Nullable protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) { // 获取异常对用的处理器, 就是@ExceptionHandler注解的方法包装, 注意参数handlerMethod, 在方法内部, 它将用来获取所在Controller的信息 ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception); if (exceptionHandlerMethod == null) { return null; } if (this.argumentResolvers != null) { exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } if (this.returnValueHandlers != null) { exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } ServletWebRequest webRequest = new ServletWebRequest(request, response); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); try { if (logger.isDebugEnabled()) { logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod); } Throwable cause = exception.getCause(); // 调用异常处理handler的方法. if (cause != null) { // Expose cause as provided argument as well exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod); } else { // Otherwise, just the given exception as-is exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod); } } catch (Throwable invocationEx) { // Any other than the original exception is unintended here, // probably an accident (e.g. failed assertion or the like). if (invocationEx != exception && logger.isWarnEnabled()) { logger.warn("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx); } // Continue with default processing of the original exception... return null; } if (mavContainer.isRequestHandled()) { return new ModelAndView(); } else { ModelMap model = mavContainer.getModel(); HttpStatus status = mavContainer.getStatus(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status); mav.setViewName(mavContainer.getViewName()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav; } }