Spring MVC 提供了多种异常处理方式,总的来说,有四种方式:
@ResponseStatus
@ExceptionHandler
@ControllerAdvice
HandlerExceptionResolver
@ResponseStatus
通常用于自定义异常上,并会产生合适的 HTTP 状态码
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such Order") // 404
public class OrderNotFoundException extends RuntimeException {
// ...
}
然后在一个控制器中使用它
@RequestMapping(value="/orders/{id}", method=GET)
public String showOrder(@PathVariable("id") long id, Model model) {
Order order = orderRepository.findOrderById(id);
if (order == null) throw new OrderNotFoundException(id);
model.addAttribute(order);
return "orderDetail";
}
当抛出 OrderNotFoundException
异常时,会返回 404 响应。
@ExceptionHandler
这个注解基于控制器的方法,当这个控制器中特定的异常被抛出时,就会被 @ExceptionHandler
所注解的方法处理
@Controller
public class ExceptionHandlingController {
// @RequestHandler methods
...
// Exception handling methods
// Convert a predefined exception to an HTTP Status code
@ResponseStatus(value=HttpStatus.CONFLICT,
reason="Data integrity violation") // 409
@ExceptionHandler(DataIntegrityViolationException.class)
public void conflict() {
// Nothing to do
}
// Specify name of a specific view that will be used to display the error:
@ExceptionHandler({SQLException.class,DataAccessException.class})
public String databaseError() {
// Nothing to do. Returns the logical view name of an error page, passed
// to the view-resolver(s) in usual way.
// Note that the exception is NOT available to this view (it is not added
// to the model) but see "Extending ExceptionHandlerExceptionResolver"
// below.
return "databaseError";
}
// Total control - setup a model and return the view name yourself. Or
// consider subclassing ExceptionHandlerExceptionResolver (see below).
@ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception ex) {
logger.error("Request: " + req.getRequestURL() + " raised " + ex);
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}
这种方式只能针对某个控制器,如果想在所有的控制器进行统一的异常处理,可以使用 @ControllerAdvice
。
通常我们想要的是全局的异常处理,而 @ResponseStatus
和 @ExceptionHandler
都有一定的局限性,所以 @ControllerAdvice
和 HandlerExceptionResolver
就比较重要了。
@ControllerAdvice
任何使用 @ControllerAdvice
的类都支持三种类型的方法:
@ExceptionHandler
异常处理方法@ModelAttribute
Model 增强方法@InitBinder
绑定器方法
我们主要关注异常处理方法。详细的说明查看 官方手册
@ControllerAdvice
类中的 @ExceptionHandler
的使用和在控制器中的使用方法一样,只是 @ControllerAdvice
的异常处理可应用于全局。
@ControllerAdvice
class GlobalControllerExceptionHandler {
@ResponseStatus(HttpStatus.CONFLICT) // 409
@ExceptionHandler(DataIntegrityViolationException.class)
public void handleConflict() {
// Nothing to do
}
}
HandlerExceptionResolver
任何实现 HandlerExceptionResolver
的 DispatcherServlet
应用程序上下文中声明的任何 Spring bean 都将用于截取和处理在 MVC 系统中引发但未由Controller处理的任何异常。 这个接口看起来像这样
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex);
}
handler
指向产生这个异常的控制器。
Spring 提供了一个简单的 HandlerExceptionResolver
实现 SimpleMappingExceptionResolver
,它提供了几个选项:
- 映射异常名到视图名 - 仅需要指定类名,不需要包名
- 为没有处理的异常指定默认的错误页面
- 输出日志信息
- 设置 Model 的
exception
属性,可用在视图上
一个典型的配置如下:
@Configuration
@EnableWebMvc // Optionally setup Spring MVC defaults (if you aren't using
// Spring Boot & haven't specified @EnableWebMvc elsewhere)
public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Bean(name="simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver
createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r =
new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");
mappings.setProperty("InvalidCreditCardException", "creditCardError");
r.setExceptionMappings(mappings); // None by default
r.setDefaultErrorView("error"); // No default
r.setExceptionAttribute("ex"); // Default is "exception"
r.setWarnLogCategory("example.MvcLogger"); // No default
return r;
}
...
}
通常我们可以通过继承 SimpleMappingExceptionResolver
来更灵活的定制自己的异常处理行为,如使用其他的 logger、覆盖默认的 log 信息等。
参考
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc