skip to content

@ExceptionHandler (Local)

A controller-local @ExceptionHandler maps exception types to responses, with Spring choosing the most specific match. Interviewers ask how resolution works when several handlers could apply, and when local beats global.

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

questions

5

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

open as a page

What method arguments and return types can a local @ExceptionHandler method declare?

level: middleimportance: should knowfreq 48%

basics

~20 s

It can take the exception itself, plus request-related things like WebRequest, HttpServletRequest/Response, HttpSession, Principal, Locale, and HandlerMethod. It returns the same values as a normal controller: ResponseEntity, an @ResponseBody object, a view name, ModelAndView, or void.

open as a page

When multiple @ExceptionHandler methods could catch a thrown exception, how does Spring choose which one runs?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Spring picks the handler whose declared exception type is the closest match in the class hierarchy to the thrown exception — the most specific one. A handler for the exact type wins over one for a superclass like RuntimeException.

open as a page

How do you control the HTTP status code and response body from a local @ExceptionHandler? Compare @ResponseStatus and ResponseEntity.

level: seniorimportance: should knowfreq 40%

basics

~10 s

Either annotate the handler with @ResponseStatus(HttpStatus.NOT_FOUND) and return the body object, or return a ResponseEntity that sets status, headers and body directly. ResponseEntity gives per-response control; @ResponseStatus is a fixed static status.

open as a page

Explain how Spring resolves and invokes local @ExceptionHandler methods internally, and what such handlers cannot catch.

level: principalimportance: nice to knowfreq 26%

basics

~20 s

DispatcherServlet delegates a thrown exception to its HandlerExceptionResolvers. ExceptionHandlerExceptionResolver finds the controller's @ExceptionHandler methods via ExceptionHandlerMethodResolver and invokes the best match. It can't catch exceptions from Servlet Filters, after the response is committed, or thrown by the handler itself.

open as a page