jac*_*lau 4 api spring spring-mvc microservices spring-restcontroller
以下是我目前面临的情况的一些事实
我最近为 Spring RestController构建了一个RestControllerAdvice具有各种ExceptionHandler全局异常处理程序的函数。
由于我想返回我的自定义响应 json 以处理 中指定的预定义 HTTP 错误ResponseEntityExceptionHandler,因此我的RestControllerAdvice类继承了,并且覆盖了, 之类的ResponseEntityExceptionHandler方法。handleHttpRequestMethodNotSupported()handleHttpMessageNotReadable()
我已经成功覆盖handleHttpMediaTypeNotSupported(),handleHttpMessageNotReadable()但是当涉及到时handleHttpRequestMethodNotSupported(),我没有这样做。
这是我的代码的摘录:
@Order(Ordered.HIGHEST_PRECEDENCE)
@RestControllerAdvice(annotations=RestController.class)
public class TestRestExceptionHandler extends ResponseEntityExceptionHandler{
@Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
BaseResponseJson response = new BaseResponseJson();
response.setRespCode(BaseResponseJson.JSON_RESP_CODE_ERROR);
response.setRespMsg("Request Method Not Supported");
return handleExceptionInternal(ex, response, headers, status, request);
}
@Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
BaseResponseJson response = new BaseResponseJson();
response.setRespCode(BaseResponseJson.JSON_RESP_CODE_ERROR);
response.setRespMsg("Message Not Readable");
return handleExceptionInternal(ex, response, headers, status, request);
}
@Override
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request){
BaseResponseJson response = new BaseResponseJson();
response.setRespCode(BaseResponseJson.JSON_RESP_CODE_ERROR);
response.setRespMsg("Media Type Not Supported");
return handleExceptionInternal(ex, response, headers, status, request);
}
}
Run Code Online (Sandbox Code Playgroud)
日志handleHttpRequestMethodNotSupported()显示如下:
[2019-06-05T17:49:50.368+0800][XNIO-74 task-7][WARN ][o.s.w.s.m.s.DefaultHandlerExceptionResolver] Resolved exception caused by Handler execution: org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
Run Code Online (Sandbox Code Playgroud)
日志handleHttpMessageNotReadable()显示如下:
[2019-06-05T17:50:21.915+0800][XNIO-74 task-8][WARN ][o.s.w.s.m.m.a.ExceptionHandlerExceptionResolver] Resolved exception caused by Handler execution
Run Code Online (Sandbox Code Playgroud)
如您所见,成功代码由 处理,ExceptionHandlerExceptionResolver而故障代码由 处理DefaultHandlerExceptionResolver。
我想知道根本原因是什么,如果有人可以推荐任何可用的解决方案,我将不胜感激。谢谢你。
从jackycflau 的回答中,我们可以总结为 2 个问题。
一季度。为什么删除
annotations=RestController.class将适用于 HttpRequestMethodNotSupportedExceptionQ2。为什么只有
HttpRequestMethodNotSupportedException没有被抓住?
要回答这两个问题,我们需要看一下 spring 如何处理异常的代码。以下源码基于spring 4.3.5。
在 springDispatcherServlet处理请求期间,当发生错误时,HandlerExceptionResolver将尝试解决异常。在给定的情况下,异常被委托给ExceptionHandlerExceptionResolver。判断用哪种方法解决异常的方法是(getExceptionHandlerMethod在ExceptionHandlerExceptionResolver.java第417行)
/**
* Find an {@code @ExceptionHandler} method for the given exception. The default
* implementation searches methods in the class hierarchy of the controller first
* and if not found, it continues searching for additional {@code @ExceptionHandler}
* methods assuming some {@linkplain ControllerAdvice @ControllerAdvice}
* Spring-managed beans were detected.
* @param handlerMethod the method where the exception was raised (may be {@code null})
* @param exception the raised exception
* @return a method to handle the exception, or {@code null}
*/
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
Class<?> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null);
if (handlerMethod != null) {
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
}
for (Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
}
}
}
return null;
}
Run Code Online (Sandbox Code Playgroud)
由于我们正在使用@RestControllerAdvice,我们只需要关注 for 循环,它决定ControllerAdviceBean使用哪个。我们可以看到该方法isApplicableToBeanType会判断是否ControllerAdviceBean适用,相关代码为(ControllerAdviceBean.java第149行)
/**
* Check whether the given bean type should be assisted by this
* {@code @ControllerAdvice} instance.
* @param beanType the type of the bean to check
* @see org.springframework.web.bind.annotation.ControllerAdvice
* @since 4.0
*/
public boolean isApplicableToBeanType(Class<?> beanType) {
if (!hasSelectors()) {
return true;
}
else if (beanType != null) {
for (String basePackage : this.basePackages) {
if (beanType.getName().startsWith(basePackage)) {
return true;
}
}
for (Class<?> clazz : this.assignableTypes) {
if (ClassUtils.isAssignable(clazz, beanType)) {
return true;
}
}
for (Class<? extends Annotation> annotationClass : this.annotations) {
if (AnnotationUtils.findAnnotation(beanType, annotationClass) != null) {
return true;
}
}
}
return false;
}
private boolean hasSelectors() {
return (!this.basePackages.isEmpty() || !this.assignableTypes.isEmpty() || !this.annotations.isEmpty());
}
Run Code Online (Sandbox Code Playgroud)
通过阅读代码,我们可以解释发生了什么:
当annotations=RestController.class被删除时,hasSelectors将返回 false,因此isApplicableToBeanType将返回 true。所以在这种情况下HttpRequestMethodNotSupportedException会被处理TestRestExceptionHandler。
对于HttpRequestMethodNotSupportedException,DispatcherSerlvet找不到处理请求的控制器方法。因此handlerMethod传递给getExceptionHandlerMethodis null,然后beanType传递给isApplicableToBeanType也是 null 并返回 false 。
另一方面,DispatcherSerlvet可以找到HttpMessageNotReadableExceptionor 的控制器方法HttpMediaTypeNotSupportedException。因此,其余控制器处理程序方法将被传递给getExceptionHandlerMethod并isApplicableToBeanType返回 true。
| 归档时间: |
|
| 查看次数: |
1243 次 |
| 最近记录: |