What are the three default HandlerExceptionResolvers Spring MVC registers, and what does each one handle?
answer
- EHER → RSER → DHER (in that order)
- EHER dispatches @ExceptionHandler / @ControllerAdvice
- RSER: @ResponseStatus + ResponseStatusException
- DHER: 405/415/406/400 framework exceptions
- first-match wins → @ExceptionHandler overrides the rest
basics
~10 sExceptionHandlerExceptionResolver (invokes @ExceptionHandler methods), ResponseStatusExceptionResolver (handles @ResponseStatus-annotated exceptions and ResponseStatusException), and DefaultHandlerExceptionResolver (maps Spring MVC's own exceptions like 405/415/400 to status codes). They run in that order.
solid answer
~40 sWith @EnableWebMvc or Spring Boot, WebMvcConfigurationSupport builds a composite of three resolvers, tried in order. First, ExceptionHandlerExceptionResolver finds a matching @ExceptionHandler method in the throwing controller or a @ControllerAdvice, invokes it, and writes whatever it returns (this is where the rich, custom error handling lives). Second, ResponseStatusExceptionResolver handles exceptions marked with @ResponseStatus and instances of ResponseStatusException, translating them into the declared status code (and optional reason). Third, DefaultHandlerExceptionResolver is Spring's built-in fallback that maps standard framework exceptions to sensible status codes — HttpRequestMethodNotSupportedException to 405, HttpMediaTypeNotSupportedException to 415, MethodArgumentNotValidException/MissingServletRequestParameterException to 400, NoHandlerFoundException to 404, and so on. Because ExceptionHandlerExceptionResolver runs first, a matching @ExceptionHandler always overrides the other two.
code
java · 19 lines// Each default resolver, illustrated by what triggers it:
// 1) ExceptionHandlerExceptionResolver -> invokes this method
@RestControllerAdvice
class ApiErrors {
@ExceptionHandler(IllegalStateException.class)
ProblemDetail onState(IllegalStateException ex) {
return ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT, ex.getMessage());
}
}
// 2) ResponseStatusExceptionResolver -> reads @ResponseStatus OR ResponseStatusException
@ResponseStatus(HttpStatus.NOT_FOUND)
class UserNotFoundException extends RuntimeException {}
// or, no annotation needed:
// throw new ResponseStatusException(HttpStatus.NOT_FOUND, "user missing");
// 3) DefaultHandlerExceptionResolver -> automatic, e.g. a GET to a POST-only
// endpoint throws HttpRequestMethodNotSupportedException -> 405, no code needed.go deeper
List the three resolvers by name and one-line purpose.
Explain the order and give the DefaultHandlerExceptionResolver status mappings (405/415/400).
Explain why the ordering produces the custom > declared > framework precedence and how to override a framework default.
Discuss ProblemDetail/ErrorResponse integration in Spring 6 and the sendError-vs-setStatus body implications.
## The default trio Spring MVC configures these three resolvers (via `WebMvcConfigurationSupport.addDefaultHandlerExceptionResolvers`, wrapped in a `HandlerExceptionResolverComposite`). Order matters — they are tried top to bottom. ### 1. ExceptionHandlerExceptionResolver (runs first) This is the powerful, extensible one. It scans for **`@ExceptionHandler`** methods — both **local** (in the controller that threw) and **global** (in a **`@ControllerAdvice`** / `@RestControllerAdvice`) — that match the thrown exception type. It resolves method arguments and return values using the same `HandlerMethodArgumentResolver`/`HandlerMethodReturnValueHandler` machinery as normal controller methods, so an `@ExceptionHandler` can return a `@ResponseBody` object, a `ResponseEntity`, a `ProblemDetail`, a view name, etc. (Authoring these handlers is a sibling topic — here the point is simply that *this resolver is what dispatches to them*.) ### 2. ResponseStatusExceptionResolver (runs second) Handles two things: - Exception classes annotated with **`@ResponseStatus(HttpStatus.XxX)`** (optionally with a `reason`). - Instances of **`ResponseStatusException`** (Spring 5+), which carries a status and reason as constructor arguments instead of an annotation — handy when you cannot annotate the exception class (e.g., throwing inline: `throw new ResponseStatusException(HttpStatus.NOT_FOUND, "user missing")`). It applies the status via `response.sendError(status, reason)` when a reason is present (which triggers the container's error page), or `response.setStatus(status)` otherwise, and returns an **empty ModelAndView**. ### 3. DefaultHandlerExceptionResolver (runs last) The framework's own safety net. It knows about the standard exceptions Spring MVC itself raises and maps each to the correct HTTP status, for example: | Exception | Status | |---|---| | `HttpRequestMethodNotSupportedException` | 405 Method Not Allowed | | `HttpMediaTypeNotSupportedException` | 415 Unsupported Media Type | | `HttpMediaTypeNotAcceptableException` | 406 Not Acceptable | | `MissingServletRequestParameterException` | 400 Bad Request | | `MethodArgumentNotValidException` | 400 Bad Request | | `MissingPathVariableException` | 500 Internal Server Error | | `NoHandlerFoundException` | 404 Not Found | | `ConversionNotSupportedException` | 500 | | `AsyncRequestTimeoutException` | 503 | In Spring 6 most of these implement `ErrorResponse`/extend `ErrorResponseException`, carrying a `ProblemDetail`. ## Why order matters Because **ExceptionHandlerExceptionResolver is first**, if you write an `@ExceptionHandler(HttpRequestMethodNotSupportedException.class)`, *your* handler wins and DefaultHandlerExceptionResolver never runs for it. Likewise a custom `@ExceptionHandler` beats `@ResponseStatus` on the same exception. This layered precedence — custom > declared status > framework default — is the whole point of the chain. ## Gotchas - If you throw a Spring MVC framework exception and expect a body, remember DefaultHandlerExceptionResolver only sets the *status* (in Boot, the body then comes from /error). To customize, catch it with `@ExceptionHandler` or extend `ResponseEntityExceptionHandler`. - `NoHandlerFoundException` is only thrown if `throwExceptionIfNoHandlerFound` is enabled (default true in modern Boot); otherwise a 404 is produced elsewhere.
- If an exception has @ResponseStatus AND a matching @ExceptionHandler exists, which one determines the response?The @ExceptionHandler, because ExceptionHandlerExceptionResolver runs before ResponseStatusExceptionResolver and short-circuits the chain once it handles the exception.
- You want a 415 to return a custom JSON body instead of the default. How?DefaultHandlerExceptionResolver only sets the status. Add an @ExceptionHandler(HttpMediaTypeNotSupportedException.class) (or override handleHttpMediaTypeNotSupported in a ResponseEntityExceptionHandler subclass) so ExceptionHandlerExceptionResolver produces the body first.
saying these in an interview costs you the question
- Claiming DefaultHandlerExceptionResolver handles your own @ExceptionHandler methods
- Saying ResponseStatusException requires the @ResponseStatus annotation (it does not)
- Thinking the three resolvers run in an arbitrary/unspecified order