skip to content

@ControllerAdvice & ResponseEntityExceptionHandler

@ControllerAdvice applies exception handlers, binder setup and model attributes across controllers, optionally scoped by package or type, and ResponseEntityExceptionHandler gives you Spring's built-in mappings. The standard answer to 'how do you get one error format across the whole API'.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What are @ControllerAdvice and @RestControllerAdvice, and how do @ExceptionHandler methods inside them work?

level: juniorimportance: must knowfreq 78%

answer

  1. advice = one place for many controllers
  2. @RestControllerAdvice = @ControllerAdvice + @ResponseBody
  3. ExceptionHandlerExceptionResolver picks most specific
  4. local handler beats global
  5. bare DTO -> 200 unless @ResponseStatus

basics

~20 s

@ControllerAdvice is a class whose @ExceptionHandler methods catch exceptions thrown by many controllers in one place, instead of repeating handlers in each controller. @RestControllerAdvice is the same but adds @ResponseBody, so returned objects become JSON.

solid answer

~40 s

@ControllerAdvice marks a class holding @ExceptionHandler (and optionally @InitBinder / @ModelAttribute) methods that apply globally across controllers, giving you one central place for cross-cutting error handling. An @ExceptionHandler method declares the exception type(s) it handles; when a matching exception propagates out of any in-scope controller, Spring MVC invokes it. The method can return a ResponseEntity (full control over status/headers/body), a view name, or a plain object. @RestControllerAdvice is a convenience meta-annotation = @ControllerAdvice + @ResponseBody, so returned objects are serialized to the response body (JSON) automatically — the standard choice for REST APIs. Spring resolves the most specific matching handler by exception type, and a handler in the controller itself wins over a global advice. Set the HTTP status with ResponseEntity, @ResponseStatus, or a ProblemDetail body.

code

java · 17 lines
java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ErrorDto handleNotFound(ResourceNotFoundException ex) {
        return new ErrorDto("NOT_FOUND", ex.getMessage());
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<ErrorDto> handleBadRequest(IllegalArgumentException ex) {
        return ResponseEntity.badRequest()
                .body(new ErrorDto("BAD_REQUEST", ex.getMessage()));
    }
}

record ErrorDto(String code, String message) {}

go deeper

for a junior

Know it centralizes error handling and that @RestControllerAdvice returns JSON.

for a middle

Explain handler resolution (most specific, local-before-global) and the ways to set status.

for a senior

Discuss scope limits (filter-chain exceptions escape it) and consistent error-envelope design.

for a principal

Frame it as the app-wide error contract boundary and where ProblemDetail/observability fit.

## The problem it solves Without global handling you would write `try/catch` or an `@ExceptionHandler` method inside every `@Controller`/`@RestController`, duplicating the same error-to-response mapping everywhere. `@ControllerAdvice` centralizes that. ## @ControllerAdvice `@ControllerAdvice` is a specialization of `@Component` (so it is a Spring bean, auto-detected by component scanning). A class annotated with it can contain three kinds of shared methods that apply to **many** controllers at once: - **`@ExceptionHandler`** methods — handle exceptions thrown from controller handler methods. - **`@InitBinder`** methods — customize data binding (register formatters/editors, restrict fields). - **`@ModelAttribute`** methods — add common attributes to the model. ## @ExceptionHandler mechanics An `@ExceptionHandler` method names the exception class(es) it handles, either via the annotation value `@ExceptionHandler(MyException.class)` or by the method's parameter type. When a controller handler method throws, Spring MVC's `ExceptionHandlerExceptionResolver` looks for a matching `@ExceptionHandler` — first among methods **local** to that controller, then among global `@ControllerAdvice` beans. The **most specific** exception type match wins (e.g. a handler for `IllegalArgumentException` beats one for `Exception` when an `IllegalArgumentException` is thrown). An `@ExceptionHandler` method can accept useful parameters (the exception itself, `WebRequest`, `HttpServletRequest`/`Response`, `HttpHeaders`, etc.) and can return: - **`ResponseEntity<T>`** — full control over status code, headers, and body. - **A plain object / DTO** — with `@RestControllerAdvice` (or a `@ResponseBody` on the method) it is serialized to JSON; status defaults to 200 unless you add `@ResponseStatus`. - **`ProblemDetail`** (Spring 6+, RFC 7807) — standardized error body. - **A `String` view name / `ModelAndView`** — for server-rendered pages. ## @RestControllerAdvice `@RestControllerAdvice` is a meta-annotation combining `@ControllerAdvice` + `@ResponseBody`. It exists so REST error handlers don't each need `@ResponseBody`: returned objects are written to the response body via `HttpMessageConverter`s (Jackson → JSON). Prefer it for JSON APIs; use plain `@ControllerAdvice` when you also render views. ## Setting the status code Three common ways: (1) return `ResponseEntity.status(HttpStatus.NOT_FOUND).body(...)`; (2) annotate the handler method with `@ResponseStatus(HttpStatus.NOT_FOUND)`; (3) return a `ProblemDetail` whose status field is honored. If you return a bare DTO with no status mechanism, you get **200 OK**, which is a classic mistake. ## Gotchas - The advice class must be **component-scanned** (or declared as a bean) to take effect. - `@ExceptionHandler` only catches exceptions that escape controller handler methods dispatched by `DispatcherServlet`; exceptions thrown in Servlet **filters** or before the dispatch (e.g. in Spring Security's filter chain) are **not** caught here. - Returning `void` from an `@ExceptionHandler` (e.g. because you wrote to the response directly) is allowed but easy to get wrong. ## When to use Use a single `@RestControllerAdvice` per application (or per bounded module) to map domain/validation exceptions to a consistent error envelope. Keep controllers thin — let them throw, and let the advice translate.

  • If an @ExceptionHandler returns a plain DTO and you forget @ResponseStatus, what status does the client get?
    200 OK — the DTO is serialized as a normal successful body. You must use ResponseEntity, @ResponseStatus, or a ProblemDetail to change it.
  • Will a global @ExceptionHandler catch an exception thrown by a Spring Security filter?
    No. It only handles exceptions that escape controller handler methods during DispatcherServlet dispatch. Filter-chain errors are handled by the filter chain (e.g. AuthenticationEntryPoint/AccessDeniedHandler), not @ControllerAdvice.

saying these in an interview costs you the question

  • Thinking @ControllerAdvice catches exceptions from Servlet filters or Spring Security's filter chain
  • Assuming a returned DTO automatically sets an error status code
  • Believing @RestControllerAdvice does something different from @ControllerAdvice besides adding @ResponseBody

context

open as a page

What does extending ResponseEntityExceptionHandler give you, and how do you customize the responses it produces?

level: middleimportance: should knowfreq 55%

basics

~20 s

ResponseEntityExceptionHandler is a base class with ready-made @ExceptionHandler methods for Spring MVC's standard exceptions (like validation and unreadable-body errors). You extend it in your @ControllerAdvice and override its protected methods to customize the response body or status.

open as a page

How do you scope a @ControllerAdvice to only some controllers using basePackages, assignableTypes, and annotations?

level: seniorimportance: should knowfreq 42%

basics

~10 s

By default a @ControllerAdvice applies to every controller. You can narrow it with its attributes: basePackages/basePackageClasses (controllers in those packages), assignableTypes (controllers of those specific types/interfaces), and annotations (controllers marked with a given annotation).

open as a page

How does Spring resolve which @ExceptionHandler runs, and how do you control ordering across multiple @ControllerAdvice beans?

level: principalimportance: should knowfreq 40%

basics

~20 s

For a thrown exception, Spring first looks for a matching @ExceptionHandler in the controller itself, then in the @ControllerAdvice beans, choosing the closest exception-type match. When several advices could match, their order (via @Order or Ordered) decides which is consulted first.

open as a page

Besides @ExceptionHandler, what do @InitBinder and @ModelAttribute methods do inside a @ControllerAdvice?

level: middleimportance: nice to knowfreq 32%

basics

~20 s

In a @ControllerAdvice, @InitBinder methods customize how request data is bound (e.g. register a date format or block certain fields) for many controllers, and @ModelAttribute methods add shared values to the model before every handler runs.

open as a page