In Spring MVC, exception handling plays a crucial role in building reliable web applications and REST APIs. Instead of exposing stack traces to users or writing repetitive try-catch blocks in controllers, Spring provides powerful annotations like @ExceptionHandler and @ControllerAdvice for centralized error handling.
Think of it as the airbag system in a car: you hope you never need it, but when an error occurs, it saves your application from crashing disastrously while providing a user-friendly error response.
Core Concepts of Exception Handling in Spring MVC
Spring MVC builds exception handling on top of Java’s core Throwable hierarchy:
Error: Serious problems not intended to be caught (e.g.,OutOfMemoryError).Exception: Recoverable conditions in applications.- Checked exceptions: Must be declared with
throws(e.g.,IOException). - Unchecked exceptions (RuntimeException): Do not need explicit declaration (e.g.,
NullPointerException).
- Checked exceptions: Must be declared with
In a web context, exceptions often arise from:
- Invalid input or missing parameters
- Database connectivity issues
- Security/authentication problems
- Business logic violations
Spring MVC provides tools to handle these consistently and gracefully.
@ExceptionHandler: Local Exception Handling
The @ExceptionHandler annotation allows handling exceptions at the controller level.
Example
@Controller
public class UserController {
@GetMapping("/user/{id}")
public String getUser(@PathVariable int id) {
if (id <= 0) {
throw new IllegalArgumentException("Invalid user ID");
}
return "user";
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException ex) {
return new ResponseEntity<>("Error: " + ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
- Here, any
IllegalArgumentExceptionthrown inUserControllerwill be intercepted by the handler method. - This keeps error handling close to the controller logic.
Limitation: Duplication across multiple controllers.
@ControllerAdvice: Global Exception Handling
@ControllerAdvice provides global exception handling across all controllers, promoting consistency and reusability.
Example
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException ex) {
return new ResponseEntity<>("Invalid input: " + ex.getMessage(), HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGeneral(Exception ex) {
return new ResponseEntity<>("Something went wrong: " + ex.getMessage(),
HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Benefits:
- Centralized exception management
- Consistent error responses across controllers
- Cleaner controller code
Combining @ExceptionHandler and @ControllerAdvice
- Use
@ExceptionHandlerfor controller-specific error handling. - Use
@ControllerAdvicefor cross-cutting concerns and global error strategy.
Together, they balance local flexibility and global consistency.
Exception Hierarchy and Matching in Spring
Spring determines which handler to use by:
- Looking for a method annotated with
@ExceptionHandlerthat matches the thrown exception. - If not found locally, it checks global handlers (
@ControllerAdvice). - Spring applies the most specific exception match (e.g.,
IllegalArgumentExceptionbeforeRuntimeException).
Best Practices
- Always log exceptions with frameworks like SLF4J or Logback.
- Design clear, meaningful error messages for API clients.
- Avoid catching
Error(e.g.,OutOfMemoryError). - Group related exception handling logic into reusable classes.
- Use custom exceptions for domain-specific errors.
Real-World Scenarios
REST API Error Handling
@ControllerAdvice
public class RestExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ApiError> handleNotFound(ResourceNotFoundException ex) {
ApiError error = new ApiError(HttpStatus.NOT_FOUND, ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
}
Clients now receive structured JSON error responses instead of raw HTML.
Database Access
Wrap SQLException into a custom exception like DatabaseException and handle it globally for clean API responses.
Security & Authentication
Return HttpStatus.UNAUTHORIZED (401) or HttpStatus.FORBIDDEN (403) in global exception handlers.
📌 What's New in Java Versions?
- Java 7+: Multi-catch, try-with-resources simplify cleanup logic.
- Java 8: Lambdas and streams—exceptions in functional interfaces must be carefully managed.
- Java 9+: Stack-Walking API for better debugging.
- Java 14+: Helpful
NullPointerExceptionswith detailed messages. - Java 21: Structured concurrency enhances exception propagation across virtual threads.
FAQ
Q1: Can I define multiple @ExceptionHandler methods for different exceptions in one controller?
Yes, each method can target a specific exception type.
Q2: What happens if multiple handlers match the same exception?
Spring picks the most specific exception handler.
Q3: Should I catch and rethrow exceptions in controllers?
Prefer throwing exceptions and letting @ControllerAdvice handle them globally.
Q4: Can I customize error response format in Spring MVC?
Yes, by returning a DTO (e.g., ApiError) as JSON/XML.
Q5: How do I log exceptions in global handlers?
Inject a logger (private static final Logger log = LoggerFactory.getLogger(...)) and log the error.
Q6: Is @ControllerAdvice only for exceptions?
No, it can also handle @InitBinder and @ModelAttribute globally.
Q7: Can @RestControllerAdvice be used instead?
Yes, it’s a variant that automatically applies @ResponseBody for JSON/XML responses.
Q8: What is the difference between checked and unchecked exceptions in Spring MVC?
Checked exceptions must be declared with throws; unchecked don’t require declaration.
Q9: How do I handle exceptions in async requests?
Spring MVC propagates them to global handlers; you can also customize via CallableProcessingInterceptor.
Q10: Should I expose stack traces in API responses?
No, hide internal details and provide meaningful error messages instead.
Conclusion and Key Takeaways
- Use
@ExceptionHandlerfor local controller exceptions. - Use
@ControllerAdvicefor global exception handling. - Provide meaningful and consistent error responses for better user experience.
- Log exceptions properly and avoid swallowing them.
- Leverage Java’s evolving features to improve debugging and error resilience.