Из ответа jackycflau мы можем суммировать 2 вопроса.
Q1.Почему удаление annotations=RestController.class
будет работать для HttpRequestMethodNotSupportedException
Q2.Почему только HttpRequestMethodNotSupportedException
не пойман?
Чтобы ответить на эти 2 вопроса, нам нужно взглянуть на код о том, как пружина обрабатывает исключения.Следующий исходный код основан на пружине 4.3.5.
Во время обработки запроса DispatcherServlet
весной, при возникновении ошибки 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;
}
Поскольку мы используем @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());
}
Читая код, мы можем объяснить, чтопроисходит:
Ответ для Q1
Когда annotations=RestController.class
удалено, hasSelectors
вернет true, и, следовательно, isApplicableToBeanType
также вернет true.Таким образом, HttpRequestMethodNotSupportedException
будет обрабатываться TestRestExceptionHandler
в этом случае.
Ответ для Q2
Для HttpRequestMethodNotSupportedException
, DispatcherSerlvet
не может найти метод контроллера для обработки запроса.Следовательно, handlerMethod
, переданное getExceptionHandlerMethod
, равно null
, затем beanType
, переданное isApplicableToBeanType
, также равно нулю и возвращается false.
С другой стороны, DispatcherSerlvet
может найти метод управления для HttpMessageNotReadableException
или HttpMediaTypeNotSupportedException
.Таким образом, метод обработчика контроллера остатка будет передан getExceptionHandlerMethod
и isApplicableToBeanType
вернет true.