What is a controller-local @ExceptionHandler method and how do you use one?
answer
- method in same controller, annotated @ExceptionHandler
- value array or infer from parameter type
- returns ResponseEntity / body / view like any controller
- local scope = one controller only
- ExceptionHandlerExceptionResolver invokes it
basics
~20 sA method inside a @Controller annotated with @ExceptionHandler. When a request-handling method in that same controller throws a matching exception, Spring calls the handler instead of letting the error escape, and its return value becomes the response.
solid answer
~40 s@ExceptionHandler marks a method inside a @Controller/@RestController that handles exceptions thrown by that controller's own request-mapping methods. You declare which exception types it catches via the annotation value (@ExceptionHandler(OrderNotFoundException.class)) or by leaving it empty and typing the exception as a method parameter. When a handler method throws, Spring's ExceptionHandlerExceptionResolver looks for a matching @ExceptionHandler in the same controller first. The handler's return value is rendered like any controller return: a ResponseEntity, an @ResponseBody object, a view name, or ModelAndView. It's scoped to this one controller only (that's what 'local' means) — for cross-controller handling you'd use @ControllerAdvice. Typical use: turn a domain exception into a clean 404/400 JSON response with a chosen status and body.
code
java · 18 lines@RestController
class OrderController {
private final OrderService service;
OrderController(OrderService service) { this.service = service; }
@GetMapping("/orders/{id}")
Order get(@PathVariable String id) {
return service.find(id); // may throw OrderNotFoundException
}
// Local handler: only catches exceptions from THIS controller
@ExceptionHandler(OrderNotFoundException.class)
ResponseEntity<ApiError> handleMissing(OrderNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ApiError("ORDER_NOT_FOUND", ex.getMessage()));
}
}go deeper
Know it's a method in the controller that turns a thrown exception into a response, declared via @ExceptionHandler.
Explain both declaration styles, the return-value options, and that local handlers are scoped to one controller.
Add that ExceptionHandlerExceptionResolver drives it, local beats global, and describe non-handled cases (filters, no match).
Discuss where this sits in the resolver chain and the trade-off between local specificity and centralized @ControllerAdvice policy.
## What it is `@ExceptionHandler` is a Spring MVC annotation placed on a method **inside a controller** (`@Controller` or `@RestController`). It defines error-handling logic that runs when a request-mapping method (`@GetMapping`, `@PostMapping`, etc.) in **the same controller** throws an exception. 'Local' means the scope is that single controller — it does not catch exceptions from other controllers. (Cross-controller/global handling is done with `@ControllerAdvice`, a separate topic.) ## How Spring wires it Spring MVC dispatches a request through `DispatcherServlet`. If the invoked controller method throws, `DispatcherServlet` does not immediately fail — it consults its registered `HandlerExceptionResolver` beans. One of them, `ExceptionHandlerExceptionResolver`, scans the controller that raised the exception for `@ExceptionHandler` methods and invokes the best match. This is all automatic once you annotate the method; there is no manual registration. ## Declaring which exceptions a handler catches Two equivalent styles: 1. **Annotation value:** `@ExceptionHandler(OrderNotFoundException.class)` (or an array: `@ExceptionHandler({A.class, B.class})`). 2. **Parameter inference:** leave the value empty and declare the exception as a parameter — `@ExceptionHandler public ... handle(OrderNotFoundException ex)`. Spring reads the type from the signature. A handler for a supertype (e.g. `RuntimeException`) also catches its subtypes, subject to most-specific matching. ## What the handler returns An `@ExceptionHandler` method returns the **same kinds of values** as a normal controller method: - `ResponseEntity<T>` — full control over status, headers, body. - An `@ResponseBody` object (in `@RestController`, `@ResponseBody` is implied) — serialized to JSON; combine with `@ResponseStatus` to set the status code. - A `String` view name or `ModelAndView` — render an error page. - `void` — you wrote the response yourself. ## Minimal example ```java @RestController class OrderController { @GetMapping("/orders/{id}") Order get(@PathVariable String id) { return service.find(id); // throws OrderNotFoundException } @ExceptionHandler(OrderNotFoundException.class) ResponseEntity<ApiError> handle(OrderNotFoundException ex) { return ResponseEntity.status(HttpStatus.NOT_FOUND) .body(new ApiError("ORDER_NOT_FOUND", ex.getMessage())); } } ``` ## When to use local vs global - **Local `@ExceptionHandler`:** the handling is specific to one controller, or you want to override the global behavior just here. Local handlers take **precedence** over `@ControllerAdvice` for the controller they live in. - **`@ControllerAdvice`:** shared handling across many controllers. ## Common gotchas - It only catches exceptions from **its own** controller's handler-method invocation — not exceptions from Servlet `Filter`s (those run outside `DispatcherServlet`). - If **no** handler matches, the exception propagates to the next `HandlerExceptionResolver` (e.g. `DefaultHandlerExceptionResolver`, or ultimately the Servlet container / `/error`). - The handler method itself can throw; that new exception is **not** re-handled by another `@ExceptionHandler` — it propagates.
- Does an @ExceptionHandler catch exceptions thrown by a different controller?No. A local @ExceptionHandler only handles exceptions raised inside the controller class it is declared in. For shared/cross-controller handling you move the same method into an @ControllerAdvice class.
- How do you tell the handler which exception type to catch?Either list types in the annotation value — @ExceptionHandler({A.class, B.class}) — or leave it empty and declare the exception as a method parameter; Spring infers the type from the signature.
saying these in an interview costs you the question
- Thinking @ExceptionHandler is global by default (it is local to one controller)
- Believing you must register it manually somewhere
- Assuming it catches Servlet Filter exceptions
- Expecting an exception thrown inside the handler to be re-handled by another @ExceptionHandler