diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java index 9aa408ab0b7..e5e2db0b6a1 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -180,8 +180,16 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti } } } - // Else only apply if there are no explicit handler mappings. - return (this.mappedHandlers == null && this.mappedHandlerClasses == null); + return !hasHandlerMappings(); + } + + /** + * Whether there are any handler mappings registered via + * {@link #setMappedHandlers(Set)} or {@link #setMappedHandlerClasses(Class[])}. + * @since 5.3 + */ + protected boolean hasHandlerMappings() { + return (this.mappedHandlers != null || this.mappedHandlerClasses != null); } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java index 3ec4abb22a2..9f4ceb43a1f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,17 +48,31 @@ public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHan handler = handlerMethod.getBean(); return super.shouldApplyTo(request, handler); } + else if (hasGlobalExceptionHandlers() && hasHandlerMappings()) { + return super.shouldApplyTo(request, handler); + } else { return false; } } + /** + * Whether this resolver has global exception handlers, e.g. not declared in + * the same class as the {@code HandlerMethod} that raised the exception and + * therefore can apply to any handler. + * @since 5.3 + */ + protected boolean hasGlobalExceptionHandlers() { + return false; + } + @Override @Nullable protected final ModelAndView doResolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { - return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex); + HandlerMethod handlerMethod = (handler instanceof HandlerMethod ? (HandlerMethod) handler : null); + return doResolveHandlerMethodException(request, response, handlerMethod, ex); } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java index cf049030062..5f7c4d6fbf7 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java @@ -380,6 +380,10 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce return handlers; } + @Override + protected boolean hasGlobalExceptionHandlers() { + return !this.exceptionHandlerAdviceCache.isEmpty(); + } /** * Find an {@code @ExceptionHandler} method and invoke it to handle the raised exception. diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java index a0a8cd0b977..b4f72177600 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java @@ -39,6 +39,7 @@ import org.springframework.http.server.ServerHttpResponse; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.ClassUtils; +import org.springframework.web.HttpRequestHandler; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestControllerAdvice; @@ -51,6 +52,7 @@ import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.support.RedirectAttributes; +import org.springframework.web.servlet.resource.ResourceHttpRequestHandler; import org.springframework.web.testfixture.servlet.MockHttpServletRequest; import org.springframework.web.testfixture.servlet.MockHttpServletResponse; import org.springframework.web.util.NestedServletException; @@ -346,6 +348,22 @@ public class ExceptionHandlerExceptionResolverTests { assertThat(this.response.getContentAsString()).isEqualTo("BasePackageTestExceptionResolver: IllegalStateException"); } + @Test // gh-22619 + void resolveExceptionViaMappedHandler() throws Exception { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyControllerAdviceConfig.class); + this.resolver.setMappedHandlerClasses(HttpRequestHandler.class); + this.resolver.setApplicationContext(ctx); + this.resolver.afterPropertiesSet(); + + IllegalStateException ex = new IllegalStateException(); + ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler(); + ModelAndView mav = this.resolver.resolveException(this.request, this.response, handler, ex); + + assertThat(mav).as("Exception was not handled").isNotNull(); + assertThat(mav.isEmpty()).isTrue(); + assertThat(this.response.getContentAsString()).isEqualTo("DefaultTestExceptionResolver: IllegalStateException"); + } + private void assertMethodProcessorCount(int resolverCount, int handlerCount) { assertThat(this.resolver.getArgumentResolvers().getResolvers().size()).isEqualTo(resolverCount);