可以通过以下方式实现共通的全局处理:
- ServletRequestListener :请求的开始和结束时执行,Spring的RequestContextListener等
- Filter :Servlet的开始和结束时执行,Spring的CharacterEncodingFilter、HiddenHttpMethodFilter等
- HandlerInterceptor :Controller方法的开始和结束时执行,Spring的LocaleChangeInterceptor等
- Spring AOP(AspectJ) :面向切面,任意注入处理。
这里只介绍 Spring MVC 内置的异常处理机制:
- @ResponseStatus
- @ExceptionHandler
- @ControllerAdvice
- HandlerExceptionResolver
(1)Controller内的异常捕获
通过注解@ExceptionHandler来实现异常的捕获处理。
@Controller public class FooController { @ExceptionHandler @ResponseStatus(NOT_FOUND) public void notFound(ResourceNotFoundException ex) { // N/A } }
也可以通过实现HandlerExceptionResolver来实现异常的捕获处理。
@Controller public class FooController implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) { // N/A } }
(2)被@ResponseStatus注解标记的异常
会被 ResponseStatusExceptionResolver 自动处理。
@ResponseStatus(value = NOT_FOUND, reason = "Resource is not found.") public class ResourceNotFoundException extends RuntimeException { // ... }
(3)全局异常捕获
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler @RespnseStatus(NOT_FOUND) public void notFound(ResourceNotFoundException ex) { // N/A } } @ControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(RuntimeException.class) public ResponseEntity<Error> handle(RuntimeException ex, HttpServletResponse res){ // N/A res.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); } }
API和页面共存的时候,全局异常捕获可以通过再次forward来返回不同的内容。
@ControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(Exception.class) public String error(HttpServletRequest req, Exception ex){ // N/A String baseUrl = getBaseUrl(req); return "forward:" + baseUrl + "/error"; } } @Controller public class GeneralErrorHandlerController { @RequestMapping("/admin/error") public String screenError(HttpServletRequest req, Model model) { // N/A return "screen/admin/error"; } @RequestMapping(value = "/api/internal/error") @ResponseBody public ErrorResponse apiError(HttpServletRequest req) { // N/A return ErrorResponse.build(e); } }
***** 不能自动设置status code,需要自己设置
***** 不能捕获所有异常,比如表示层,404错误等
@ControllerAdvice注解内的@ExceptionHandler、@InitBinder、@ModelAttribute方法会被应用到所有的@RequestMapping注解的方法。上边@ExceptionHandler是最常见的,而@InitBinder也会用到,比如:
1)默认SpringMVC对空字段设置值为空字符串而不是null。通过以下定义可以将空字符串设置成null,并且会对字符串进行自动trim处理。
还可以传入需要过滤的字符:StringTrimmerEditor(String charsToDelete, boolean emptyAsNull)
@ControllerAdvice public class NullValidAdvice { @InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(String.class, new StringTrimmerEditor(true)); } }
2)设置JSONP的返回参数
@ControllerAdvice public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { public JsonpAdvice() { super("callback"); } }
(4)web.xml中的error-page
这部分本身不归Spring处理,是ServletContainer处理范围。
把所有未能捕获的异常再次跳转到一个指定mapping。
<error-page> <location>/error</location> </error-page>
@Controller class GeneralErrorHandlerController { @RequestMapping("error") public String otherError(HttpServletRequest req, Model model) { // N/A } }
(5)自定义404相应
设置throwExceptionIfNoHandlerFound为true
<servlet> <servlet-name>rest-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>throwExceptionIfNoHandlerFound</param-name> <param-value>true</param-value> </init-param> </servlet>
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(NoHandlerFoundException.class) public ResponseEntity<Error> handle(NoHandlerFoundException ex){ // N/A } }
(6)自定义错误页面
定制将异常映射为视图 SimpleMappingExceptionResolver
@Bean public SimpleMappingExceptionResolver exceptionResolver(){ SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties exceptions = new Properties(); exceptions.put(ArithmeticException.class, "error"); resolver.setExceptionMappings(exceptions); return resolver; }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Spring MVC Exception Handling</title> </head> <body> <h1>Spring MVC Exception Handling</h1> ${exception.message} </body> </html>
***** 测试开发环境可以在页面打印出异常堆栈,方便调试。
Spring MVC 异常处理顺序:
ExceptionHandlerExceptionResolver :被@ExceptionHandler注解标记的异常
->
ResponseStatusExceptionResolver :被@ResponseStatus注解标记的异常
->
DefaultHandlerExceptionResolver :Spring的标准异常处理
->
自定义的Resolver
标准异常处理 DefaultHandlerExceptionResolver
引用
BindException 400 (Bad Request)
ConversionNotSupportedException 500 (Internal Server Error)
HttpMediaTypeNotAcceptableException 406 (Not Acceptable)
HttpMediaTypeNotSupportedException 415 (Unsupported Media Type)
HttpMessageNotReadableException 400 (Bad Request)
HttpMessageNotWritableException 500 (Internal Server Error)
HttpRequestMethodNotSupportedException 405 (Method Not Allowed)
MethodArgumentNotValidException 400 (Bad Request)
MissingPathVariableException 500 (Internal Server Error)
MissingServletRequestParameterException 400 (Bad Request)
MissingServletRequestPartException 400 (Bad Request)
NoHandlerFoundException 404 (Not Found)
NoSuchRequestHandlingMethodException 404 (Not Found)
TypeMismatchException 400 (Bad Request)
ConversionNotSupportedException 500 (Internal Server Error)
HttpMediaTypeNotAcceptableException 406 (Not Acceptable)
HttpMediaTypeNotSupportedException 415 (Unsupported Media Type)
HttpMessageNotReadableException 400 (Bad Request)
HttpMessageNotWritableException 500 (Internal Server Error)
HttpRequestMethodNotSupportedException 405 (Method Not Allowed)
MethodArgumentNotValidException 400 (Bad Request)
MissingPathVariableException 500 (Internal Server Error)
MissingServletRequestParameterException 400 (Bad Request)
MissingServletRequestPartException 400 (Bad Request)
NoHandlerFoundException 404 (Not Found)
NoSuchRequestHandlingMethodException 404 (Not Found)
TypeMismatchException 400 (Bad Request)
参考:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-exceptionhandlers
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
http://memorynotfound.com/spring-mvc-exception-handling/
http://www.journaldev.com/2651/spring-mvc-exception-handling-controlleradvice-exceptionhandler-handlerexceptionresolver
http://www.roytuts.com/exception-handling-best-practices-in-java/
https://speakerdeck.com/sinsengumi/spring-boot-application-infrastructure
https://github.com/jirutka/spring-rest-exception-handler
http://qiita.com/kazuki43zoo/items/757b557c05f548c6c5db
http://cgs1999.iteye.com/blog/1547197
http://www.cnblogs.com/xinzhao/p/4934247.html