Browse Source

Explicit coverage of root vs cause exception matching in MVC ref docs

Issue: SPR-16743
pull/1801/merge
Juergen Hoeller 8 years ago
parent
commit
a200df6c8d
  1. 8
      spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java
  2. 21
      spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java
  3. 32
      src/docs/asciidoc/web/webflux.adoc
  4. 72
      src/docs/asciidoc/web/webmvc.adoc

8
spring-webmvc/src/main/java/org/springframework/web/servlet/config/AnnotationDrivenBeanDefinitionParser.java

@ -63,8 +63,6 @@ import org.springframework.util.xml.DomUtils;
import org.springframework.web.HttpRequestHandler; import org.springframework.web.HttpRequestHandler;
import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.accept.ContentNegotiationManagerFactoryBean; import org.springframework.web.accept.ContentNegotiationManagerFactoryBean;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.bind.support.WebArgumentResolver; import org.springframework.web.bind.support.WebArgumentResolver;
import org.springframework.web.method.support.CompositeUriComponentsContributor; import org.springframework.web.method.support.CompositeUriComponentsContributor;
@ -115,10 +113,10 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
* *
* <p>This class registers the following {@link HandlerExceptionResolver}s: * <p>This class registers the following {@link HandlerExceptionResolver}s:
* <ul> * <ul>
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions * <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
* through @{@link ExceptionHandler} methods. * {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated * <li>{@link ResponseStatusExceptionResolver} for exceptions annotated
* with @{@link ResponseStatus}. * with {@link org.springframework.web.bind.annotation.ResponseStatus}.
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring * <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
* exception types * exception types
* </ul> * </ul>

21
spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java

@ -67,8 +67,6 @@ import org.springframework.validation.Validator;
import org.springframework.web.HttpRequestHandler; import org.springframework.web.HttpRequestHandler;
import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.context.ServletContextAware; import org.springframework.web.context.ServletContextAware;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
@ -136,10 +134,10 @@ import org.springframework.web.util.UrlPathHelper;
* <p>Registers a {@link HandlerExceptionResolverComposite} with this chain of * <p>Registers a {@link HandlerExceptionResolverComposite} with this chain of
* exception resolvers: * exception resolvers:
* <ul> * <ul>
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions * <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
* through @{@link ExceptionHandler} methods. * {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated * <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with
* with @{@link ResponseStatus}. * {@link org.springframework.web.bind.annotation.ResponseStatus}.
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring * <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
* exception types * exception types
* </ul> * </ul>
@ -926,12 +924,11 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
* A method available to subclasses for adding default {@link HandlerExceptionResolver}s. * A method available to subclasses for adding default {@link HandlerExceptionResolver}s.
* <p>Adds the following exception resolvers: * <p>Adds the following exception resolvers:
* <ul> * <ul>
* <li>{@link ExceptionHandlerExceptionResolver} * <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
* for handling exceptions through @{@link ExceptionHandler} methods. * {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
* <li>{@link ResponseStatusExceptionResolver} * <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with
* for exceptions annotated with @{@link ResponseStatus}. * {@link org.springframework.web.bind.annotation.ResponseStatus}.
* <li>{@link DefaultHandlerExceptionResolver} * <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring exception types
* for resolving known Spring exception types
* </ul> * </ul>
*/ */
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {

32
src/docs/asciidoc/web/webflux.adoc

@ -2406,33 +2406,31 @@ controller-specific ``Formatter``'s:
public ResponseEntity<String> handle(IOException ex) { public ResponseEntity<String> handle(IOException ex) {
// ... // ...
} }
} }
---- ----
The annotation can list the exception types to match. Or simply declare the target The exception may match against a top-level exception being propagated (i.e. a direct
exception as a method argument as shown above. When multiple exception methods match, `IOException` thrown), or against the immediate cause within a top-level wrapper exception
a root exception match is generally preferred to a cause exception match. More formally (e.g. an `IOException` wrapped inside an `IllegalStateException`).
the `ExceptionDepthComparator` is used to sort exceptions based on their depth from the
thrown exception type.
In a multi-`@ControllerAdvice` arrangement, please declare your primary root exception For matching exception types, preferably declare the target exception as a method argument
mappings on a `@ControllerAdvice` prioritized with a corresponding order. While a root as shown above. Alternatively, the annotation declaration may narrow the exception types to
exception match is preferred to a cause, this is mainly among the methods of a given match. We generally recommend to be as specific as possible in the argument signature and to
controller or `@ControllerAdvice`. That means a cause match on a higher-priority declare your primary root exception mappings on a `@ControllerAdvice` prioritized with a
`@ControllerAdvice` is preferred to any match (e.g. root) on a lower-priority corresponding order. See <<web.adoc#mvc-ann-exceptionhandler,the MVC section>> for details.
`@ControllerAdvice`.
[NOTE]
====
An `@ExceptionHandler` method in WebFlux supports the same method arguments and
return values as an `@RequestMapping` method, with the exception of request body
and `@ModelAttribute` related method arguments.
====
Support for `@ExceptionHandler` methods in Spring WebFlux is provided by the Support for `@ExceptionHandler` methods in Spring WebFlux is provided by the
`HandlerAdapter` for `@RequestMapping` methods. See <<webflux-dispatcher-exceptions>> `HandlerAdapter` for `@RequestMapping` methods. See <<webflux-dispatcher-exceptions>>
under the `DispatcherHandler` section for more details. under the `DispatcherHandler` section for more details.
An `@ExceptionHandler` method in WebFlux supports the same method arguments and return
values as an `@RequestMapping` method does with the exception of request body and
`@ModelAttribute` related method arguments.
[[webflux-ann-rest-exceptions]] [[webflux-ann-rest-exceptions]]
==== REST API exceptions ==== REST API exceptions
[.small]#<<web.adoc#mvc-ann-rest-exceptions,Same in Spring MVC>># [.small]#<<web.adoc#mvc-ann-rest-exceptions,Same in Spring MVC>>#

72
src/docs/asciidoc/web/webmvc.adoc

@ -481,7 +481,6 @@ initialization parameters ( `init-param` elements) to the Servlet declaration in
Note that if <<mvc-default-servlet-handler,default servlet handling>> is Note that if <<mvc-default-servlet-handler,default servlet handling>> is
also configured, then unresolved requests are always forwarded to the default servlet also configured, then unresolved requests are always forwarded to the default servlet
and a 404 would never be raised. and a 404 would never be raised.
|=== |===
@ -2830,22 +2829,75 @@ controller-specific ``Formatter``'s:
public ResponseEntity<String> handle(IOException ex) { public ResponseEntity<String> handle(IOException ex) {
// ... // ...
} }
}
----
The exception may match against a top-level exception being propagated (i.e. a direct
`IOException` thrown), or against the immediate cause within a top-level wrapper exception
(e.g. an `IOException` wrapped inside an `IllegalStateException`).
For matching exception types, preferably declare the target exception as a method argument
as shown above. When multiple exception methods match, a root exception match is generally
preferred to a cause exception match. More specifically, the `ExceptionDepthComparator` is
used to sort exceptions based on their depth from the thrown exception type.
Alternatively, the annotation declaration may narrow the exception types to match:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(IOException ex) {
// ...
}
----
Or even a list of specific exception types with a very generic argument signature:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
// ...
} }
---- ----
The annotation can list the exception types to match. Or simply declare the target [NOTE]
exception as a method argument as shown above. When multiple exception methods match, ====
a root exception match is generally preferred to a cause exception match. More formally The distinction between root and cause exception matching can be surprising:
the `ExceptionDepthComparator` is used to sort exceptions based on their depth from the
thrown exception type. In the `IOException` variant above, the method will typically be called with
the actual `FileSystemException` or `RemoteException` instance as the argument
since both of them extend from `IOException`. However, if any such matching
exception is propagated within a wrapper exception which is an `IOException`
itself, the passed-in exception instance will be that wrapper exception.
The behavior is even simpler in the `handle(Exception)` variant: This will
always be invoked with the wrapper exception in a wrapping scenario, with the
actually matching exception to be found through `ex.getCause()` in that case.
The passed-in exception will only be the actual `FileSystemException` or
`RemoteException` instance when these are thrown as top-level exceptions.
====
We generally recommend to be as specific as possible in the argument signature,
reducing the potential for mismatches between root and cause exception types.
Consider breaking a multi-matching method into individual `@ExceptionHandler`
methods, each matching a single specific exception type through its signature.
In a multi-`@ControllerAdvice` arrangement, please declare your primary root exception In a multi-`@ControllerAdvice` arrangement, please declare your primary root exception
mappings on a `@ControllerAdvice` prioritized with a corresponding order. While a root mappings on a `@ControllerAdvice` prioritized with a corresponding order. While a root
exception match is preferred to a cause, this is mainly among the methods of a given exception match is preferred to a cause, this is defined among the methods of a given
controller or `@ControllerAdvice`. That means a cause match on a higher-priority controller or `@ControllerAdvice` class. This means a cause match on a higher-priority
`@ControllerAdvice` is preferred to any match (e.g. root) on a lower-priority `@ControllerAdvice` bean is preferred to any match (e.g. root) on a lower-priority
`@ControllerAdvice`. `@ControllerAdvice` bean.
Last but not least, an `@ExceptionHandler` method implementation may choose to back
out of dealing with a given exception instance by rethrowing it in its original form.
This is useful in scenarios where you are only interested in root-level matches or in
matches within a specific context that cannot be statically determined. A rethrown
exception will be propagated through the remaining resolution chain, just like if
the given `@ExceptionHandler` method would not have matched in the first place.
Support for `@ExceptionHandler` methods in Spring MVC is built on the `DispatcherServlet` Support for `@ExceptionHandler` methods in Spring MVC is built on the `DispatcherServlet`
level, <<mvc-exceptionhandlers,HandlerExceptionResolver>> mechanism. level, <<mvc-exceptionhandlers,HandlerExceptionResolver>> mechanism.

Loading…
Cancel
Save