What are @ControllerAdvice and @RestControllerAdvice, and how do @ExceptionHandler methods inside them work?
answer
- advice = one place for many controllers
- @RestControllerAdvice = @ControllerAdvice + @ResponseBody
- ExceptionHandlerExceptionResolver picks most specific
- local handler beats global
- 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@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
Know it centralizes error handling and that @RestControllerAdvice returns JSON.
Explain handler resolution (most specific, local-before-global) and the ways to set status.
Discuss scope limits (filter-chain exceptions escape it) and consistent error-envelope design.
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