SpringMVC异常统一处理的几种方式 - TFdream/blog GitHub Wiki

Spring 统一异常处理有 3 种方式,分别为:

  • 使用HTTP 状态码;
  • 使用 @ExceptionHandler 注解;
  • 使用 @ControllerAdvice注解;
  • 实现 HandlerExceptionResolver 接口.

1. 使用HTTP 状态码

@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such Order")  // 404
 public class OrderNotFoundException extends RuntimeException {
     // ...
 }

在Controller中使用:

 @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";
 }

2. 使用 @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;
  }
}

3. 使用@ControllerAdvice

上述两种方式都是基于单个Controller的,我们还可以通过 @ControllerAdvice进行全局异常处理。

@ControllerAdvice
public class RestResponseEntityExceptionHandler extends
        ResponseEntityExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(getClass());
    private ObjectMapper mapper;

    @ExceptionHandler(Exception.class)
    protected ResponseEntity<Object> handleExceptions(Exception ex,
                                                      WebRequest request) {

        HttpStatus status = HttpStatus.OK;
        HttpHeaders headers = new HttpHeaders();
        String bodyOfResponse = "";

		ServiceResult result = new ServiceResult();
        if(ex instanceof ValidationException){
            result.setCode(999);
            result.setMsg(ex.getMessage());
        } else {
        	logger.error(ex.getMessage(), ex);

            result.setCode(500);
            result.setMsg(ex.getMessage());
        }
        
        try {
            bodyOfResponse = mapper.writeValueAsString(result);
        } catch (IOException e) {
        }

		headers.add("Content-Type", "application/json;charset=UTF-8");

        return handleExceptionInternal(ex, bodyOfResponse, headers, status,request);
    }

    @Resource(name = "jsonMapper")
    public void setMapper(ObjectMapper mapper) {
        this.mapper = mapper;
    }
}

ValidationException如下:

public class ValidationException extends RuntimeException {
    public ValidationException(String message){
        super(message);
    }
}

4. 使用HandlerExceptionResolver

@Component  
public class DemoHandlerExceptionResolver implements HandlerExceptionResolver{  

    /**
     *
     */   
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,  
            Exception ex) {  
        System.out.println("This is exception handler method!");  
        return null;  
    }
}

参考资料

Exception Handling in Spring MVC

⚠️ **GitHub.com Fallback** ⚠️