skip to content

What is a controller-local @ExceptionHandler method and how do you use one?

level: juniorimportance: must knowfreq 72%

answer

  1. method in same controller, annotated @ExceptionHandler
  2. value array or infer from parameter type
  3. returns ResponseEntity / body / view like any controller
  4. local scope = one controller only
  5. ExceptionHandlerExceptionResolver invokes it

basics

~20 s

A 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
java
@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

for a junior

Know it's a method in the controller that turns a thrown exception into a response, declared via @ExceptionHandler.

for a middle

Explain both declaration styles, the return-value options, and that local handlers are scoped to one controller.

for a senior

Add that ExceptionHandlerExceptionResolver drives it, local beats global, and describe non-handled cases (filters, no match).

for a principal

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

context