Spring MVC 异常处理

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 都有一定的局限性,所以 @ControllerAdviceHandlerExceptionResolver 就比较重要了。

@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

任何实现 HandlerExceptionResolverDispatcherServlet应用程序上下文中声明的任何 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值