Exception Handling with @ControllerAdvice

@ControllerAdvice centralizes error handling across every controller. With @ExceptionHandler methods returning ResponseEntity error bodies — or a quick ResponseStatusException — your API speaks in clean, consistent status codes.

Learn Exception Handling with @ControllerAdvice in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise…

Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You should understand Java exceptions and how Spring REST controllers return responses. The validation lesson pairs well with this one.

💡 Analogy: Without central handling, every office (controller) keeps its own first-aid kit and gives different answers when something goes wrong. @ControllerAdvice is a single help desk for the whole building: whenever any office raises a problem, it routes to the right specialist ( @ExceptionHandler ) who responds with a consistent, well-formatted answer — the right status code and a tidy error body — every time.

Controllers stay focused on the happy path; the advice handles the unhappy ones.

Annotate a class with @RestControllerAdvice and add an @ExceptionHandler method per error type. Each returns a ResponseEntity with the right status and a structured body. A catch-all prevents raw stack traces from leaking.

Throw a custom domain exception (handled by the advice) for reusable, meaningful errors, or a ResponseStatusException inline for quick one-offs. Both end up as the correct HTTP status.

Spring picks the handler whose exception type best matches what was thrown, falling back to a catch-all. Here is that "match by type" dispatch in plain Java you can run.

Answer: the more specific ResourceNotFoundException handler; Exception is the fallback.

Answer: @ResponseBody — return values are serialized as the JSON response body.

🎯 YOUR TURN — Map a 409 Conflict

Add an @ExceptionHandler for a DuplicateEmailException that returns 409 Conflict .

🧩 MINI-CHALLENGE — One handler, two exceptions

Handle both IllegalArgumentException and IllegalStateException with a single handler returning 400.

You can now centralize error handling with @ControllerAdvice , map exception types with @ExceptionHandler , return precise ResponseEntity error bodies, throw ResponseStatusException inline, and pick correct HTTP status codes.

Next up: DTOs & Mapping — why you should not expose entities, and how to convert between them cleanly.

Practice quiz

What does a class annotated with @ControllerAdvice provide?

  • Cross-cutting exception handling shared across controllers
  • A new controller endpoint
  • A database connection
  • A scheduled task

Answer: Cross-cutting exception handling shared across controllers. @ControllerAdvice centralizes exception handling (and other advice) across all controllers in one place.

What does @ExceptionHandler(SomeException.class) do?

  • Throws the exception
  • Maps that exception type to a handler method
  • Logs only
  • Disables the exception

Answer: Maps that exception type to a handler method. @ExceptionHandler binds a method to handle the listed exception type(s) when thrown by a controller.

What is the benefit of returning ResponseEntity from a handler?

  • It hides the body
  • It is required by Java
  • It caches the response
  • You control both the HTTP status and the response body

Answer: You control both the HTTP status and the response body. ResponseEntity lets you set the status code, headers, and body explicitly for the error response.

What does throwing a ResponseStatusException(HttpStatus.NOT_FOUND) result in?

  • A 200 response
  • A 404 response with that status
  • A compile error
  • A redirect

Answer: A 404 response with that status. ResponseStatusException maps directly to the given HTTP status, here 404 Not Found.

Which combination makes a @RestController-style advice that returns JSON bodies?

  • @Controller only
  • @ControllerAdvice + @ResponseBody, or @RestControllerAdvice
  • @Service
  • @Component only

Answer: @ControllerAdvice + @ResponseBody, or @RestControllerAdvice. @RestControllerAdvice = @ControllerAdvice + @ResponseBody, so handler return values become JSON bodies.

If two @ExceptionHandler methods could match, which is chosen?

  • A random one
  • The one for the most specific exception type
  • The first declared
  • Both run

Answer: The one for the most specific exception type. Spring picks the handler whose declared exception type is the most specific match for the thrown exception.

What status is conventional for a failed bean validation on a request body?

  • 201 Created
  • 403 Forbidden
  • 400 Bad Request
  • 500 Internal Server Error

Answer: 400 Bad Request. Invalid input is a client error, conventionally returned as 400 Bad Request.

Why centralize error handling in @ControllerAdvice instead of try/catch everywhere?

  • It runs faster
  • It avoids duplicated handling and gives consistent error responses
  • It removes exceptions
  • It is mandatory

Answer: It avoids duplicated handling and gives consistent error responses. Centralizing avoids repeating try/catch in every controller and yields uniform, consistent error responses.

What HTTP status best fits an unexpected server-side failure?

  • 400
  • 404
  • 500 Internal Server Error
  • 204

Answer: 500 Internal Server Error. Unhandled or unexpected server errors map to 500 Internal Server Error.

Can a single @ExceptionHandler handle multiple exception types?

  • No, only one
  • Only via inheritance
  • Only with reflection
  • Yes, by listing several classes in the annotation

Answer: Yes, by listing several classes in the annotation. You can pass an array of exception classes to @ExceptionHandler to handle several types with one method.