Spring MVC 确实为您配置了一个异常处理程序。
默认情况下,DefaultHandlerExceptionResolver 使用类 javadoc 中的说明:
HandlerExceptionResolver 接口的默认实现
解决标准 Spring 异常并将它们转换为
对应的 HTTP 状态码。
这个异常解析器在常见的Spring中默认开启
org.springframework.web.servlet.DispatcherServlet.
这对 MVC 控制器来说是正确的。
但是对于 REST 控制器的异常处理程序(您的要求在这里),Spring 依赖于 ResponseEntityExceptionHandler 类。
第一个类有返回ModelAndViews 的方法,而第二个类有返回ReponseEntitys 的方法。
在这两种情况(MVC 和 REST 控制器)中,您都可以通过使用 @ControllerAdvice 注释您的类来定义自定义异常处理程序,但由于您的要求是 REST 控制器,所以让我们专注于这一点。
除了使用@ControllerAdvice 注释自定义异常处理程序之外,您还可以使用它来扩展基本异常处理程序类,例如ResponseEntityExceptionHandler 以覆盖某些行为。
ResponseEntityExceptionHandler 实现允许实际了解所有异常处理和映射。看handleException()方法,它是ResponseEntityExceptionHandler类的门面方法:
/**
* Provides handling for standard Spring MVC exceptions.
* @param ex the target exception
* @param request the current request
*/
@ExceptionHandler({
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
HttpMediaTypeNotAcceptableException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
MissingServletRequestPartException.class,
BindException.class,
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class
})
@Nullable
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
HttpHeaders headers = new HttpHeaders();
if (ex instanceof HttpRequestMethodNotSupportedException) {
HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof HttpMediaTypeNotSupportedException) {
HttpStatus status = HttpStatus.UNSUPPORTED_MEDIA_TYPE;
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof HttpMediaTypeNotAcceptableException) {
HttpStatus status = HttpStatus.NOT_ACCEPTABLE;
return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, headers, status, request);
}
else if (ex instanceof MissingPathVariableException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleMissingPathVariable((MissingPathVariableException) ex, headers, status, request);
}
else if (ex instanceof MissingServletRequestParameterException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, headers, status, request);
}
else if (ex instanceof ServletRequestBindingException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleServletRequestBindingException((ServletRequestBindingException) ex, headers, status, request);
}
else if (ex instanceof ConversionNotSupportedException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleConversionNotSupported((ConversionNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof TypeMismatchException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleTypeMismatch((TypeMismatchException) ex, headers, status, request);
}
else if (ex instanceof HttpMessageNotReadableException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, headers, status, request);
}
else if (ex instanceof HttpMessageNotWritableException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, headers, status, request);
}
else if (ex instanceof MethodArgumentNotValidException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMethodArgumentNotValid((MethodArgumentNotValidException) ex, headers, status, request);
}
else if (ex instanceof MissingServletRequestPartException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMissingServletRequestPart((MissingServletRequestPartException) ex, headers, status, request);
}
else if (ex instanceof BindException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleBindException((BindException) ex, headers, status, request);
}
else if (ex instanceof NoHandlerFoundException) {
HttpStatus status = HttpStatus.NOT_FOUND;
return handleNoHandlerFoundException((NoHandlerFoundException) ex, headers, status, request);
}
else if (ex instanceof AsyncRequestTimeoutException) {
HttpStatus status = HttpStatus.SERVICE_UNAVAILABLE;
return handleAsyncRequestTimeoutException(
(AsyncRequestTimeoutException) ex, headers, status, request);
}
else {
if (logger.isWarnEnabled()) {
logger.warn("Unknown exception type: " + ex.getClass().getName());
}
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleExceptionInternal(ex, null, headers, status, request);
}
}
所以问题是:如何覆盖特定异常的异常处理程序?
这种方法行不通:
@ExceptionHandler(value = { HttpRequestMethodNotSupportedException.class })
protected ResponseEntity<Object> handleConflict(HttpRequestMethodNotSupportedException ex, WebRequest request) {
...
}
因为在异常处理程序类中,Spring 不允许您为特定的Exception 子类定义多次映射。因此,不允许在自定义异常处理程序中添加此映射,因为 Spring 已经在 ResponseEntityExceptionHandler 类中定义了该异常的映射。
具体来说就是会阻止Spring容器启动成功。
你应该得到一个异常,例如:
引起:java.lang.IllegalStateException:为 [class org.springframework.web.HttpRequestMethodNotSupportedException] 映射的不明确的 @ExceptionHandler 方法:{protected org.springframework ...
为了简化客户端子类覆盖特定异常的实际处理/映射,Spring 在 ResponseEntityExceptionHandler 类的 protected 方法中实现了每个异常的逻辑,由它自己捕获和处理.
所以在你的情况下(覆盖HttpRequestMethodNotSupportedException的处理程序),只需覆盖你正在寻找的handleHttpRequestMethodNotSupported():
if (ex instanceof HttpRequestMethodNotSupportedException) {
HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
}
例如这样:
@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status,
WebRequest request) {
// do your processing
...
// go on (or no) executing the logic defined in the base class
return super.handleHttpRequestMethodNotSupported(ex, headers, status, request);
}
}