Exception Handling in Spring MVC

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

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

1. 使用HTTP 状态码

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

在Controller中使用:

1
2
3
4
5
6
7
8
9
@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 注解

用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@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进行全局异常处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
@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如下:

1
2
3
4
5
public class ValidationException extends RuntimeException {
public ValidationException(String message){
super(message);
}
}

4. 使用HandlerExceptionResolver

1
2
3
4
5
6
7
8
9
10
11
12
@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