Global Exception Handling with @ControllerAdvice in Spring Boot
Error handling is an essential part of building robust and user-friendly applications. Without proper exception management, users could be greeted with cryptic error messages or unclear feedback, leading to frustration and mistrust. Enter @ControllerAdvice
, a powerful Spring Boot feature that facilitates centralized and global exception handling in your web applications.
This guide explores how @ControllerAdvice
helps you manage exceptions across your Spring Boot application. We’ll highlight its features, share best practices, and compare it to local exception handling methods. Additionally, you’ll see a practical implementation with a custom exception and ResponseEntity
.
By the end of this post, you’ll be equipped to streamline exception handling and enhance your application’s resilience and usability.
Table of Contents
- What is @ControllerAdvice in Spring Boot?
- Centralized Exception Logic
- Example with Custom Exception and ResponseEntity
- Benefits of Global vs Local Handling
- Summary
What is @ControllerAdvice in Spring Boot?
Definition
@ControllerAdvice
is a Spring annotation that allows you to handle exceptions across your entire application in one central place. Rather than scattering exception handling logic across multiple controllers, @ControllerAdvice
enables a clean separation of concerns by centralizing error handling into a dedicated class.
How Does It Work?
When an exception occurs, Spring scans for any @ExceptionHandler
methods defined within a @ControllerAdvice
class. If it finds a matching handler for the exception type, it executes that method, returning the defined response to the user.
Here’s the basic syntax for a @ControllerAdvice
class:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleAllExceptions(Exception ex) {
return new ResponseEntity<>("An error occurred.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Key Features
- Global Scope: Applies to all controllers within the application.
- Flexibility: Capable of handling specific or generic exceptions.
- Custom Responses: Lets you define meaningful responses tailored to each exception type.
Whether you’re working on a small project or a large-scale application, @ControllerAdvice
streamlines error management and improves code maintainability.
Centralized Exception Logic
One of the primary advantages of using @ControllerAdvice
is the ability to centralize your exception-handling logic. This section examines why centralized handling is a game changer.
Local Handling vs Centralized Handling
By default, Spring Boot applications can handle exceptions locally within individual controllers through the use of the @ExceptionHandler
annotation. While this approach works for specific use cases, it can lead to duplicating similar exception-handling code across multiple controllers.
Example of Local Handling:
@RestController
public class ProductController {
@GetMapping("/{id}")
public Product getProduct(@PathVariable Long id) {
if (id < 1000) {
throw new IllegalArgumentException("Invalid Product ID");
}
return productService.findProductById(id);
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
While this works for handling exceptions for each controller, it becomes unwieldy for applications with many different controllers and shared exception types.
With @ControllerAdvice
, you can extract all exception-handling logic into one global class.
Centralized Logic in Practice
Here’s how we can consolidate the previous exception-handling example using @ControllerAdvice
:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
This class now handles IllegalArgumentException
for all controllers, reducing redundancy and ensuring consistent error responses across your application.
Benefits of Centralized Exception Handling
- Consistency: Ensures all exceptions are handled in the same way, improving user experience.
- Maintainability: Reduces duplicate code by centralizing error management.
- Scalability: Easier to add or modify exception-handling logic as your application grows.
By centralizing exception handling, you maintain cleaner code and a better user interface.
Example with Custom Exception and ResponseEntity
Handling exceptions becomes even more impactful when paired with custom exceptions and comprehensive JSON responses. This section illustrates how to craft a custom exception and respond with a well-structured JSON body using ResponseEntity
.
Step 1: Define a Custom Exception
First, create a custom exception class for a specific use case:
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
Step 2: Handle the Exception Globally
Next, define a method in your @ControllerAdvice
class to handle the ResourceNotFoundException
:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<Map<String, Object>> handleResourceNotFound(ResourceNotFoundException ex) {
Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("timestamp", LocalDateTime.now());
errorDetails.put("message", ex.getMessage());
errorDetails.put("status", HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
}
Step 3: Trigger the Exception
Finally, throw the exception in a controller method:
@RestController
@RequestMapping("/api/resources")
public class ResourceController {
@GetMapping("/{id}")
public Resource getResource(@PathVariable Long id) {
if (id > 1000) {
throw new ResourceNotFoundException("Resource with ID " + id + " not found.");
}
return resourceService.findById(id);
}
}
Sample JSON Response
When the ResourceNotFoundException
is triggered, the client receives a structured JSON response:
{
"timestamp": "2025-06-12T18:45:12",
"message": "Resource with ID 1500 not found.",
"status": 404
}
This response is both readable and helpful for users and developers.
Benefits of Global vs Local Handling
Both global (@ControllerAdvice
) and local (@ExceptionHandler
) handling provide value in different scenarios. Here’s a comparison to help you choose the right approach.
Feature | Global Handling (@ControllerAdvice ) | Local Handling (@ExceptionHandler ) |
---|---|---|
Scope | Applies to all controllers. | Applies to a single controller. |
Code Duplication | Eliminates repeated code. | May lead to duplicate handling logic. |
Maintainability | Easier to manage and update. | Less maintainable for large applications. |
Use Case | Shared exceptions across the app. | Controller-specific exceptions. |
When to Use Each
- Global Handling:
- Suitable for shared exception logic (e.g.,
NullPointerException
,ResourceNotFoundException
). - Ideal for large, multi-controller applications.
- Suitable for shared exception logic (e.g.,
- Local Handling:
- Best for controller-specific exceptions.
- Useful for exceptions that require unique responses per controller.
By combining both approaches thoughtfully, you can strike a balance between flexibility and simplicity.
Summary
Exception handling is a crucial feature for any application, ensuring errors are managed predictably and users receive meaningful feedback. Here’s what we discussed:
- What is
@ControllerAdvice
?: It centralizes exception-handling logic, ensuring consistent application-wide error responses. - Centralized Exception Logic: Organize all error-handling logic centrally, reducing code duplication and increasing maintainability.
- Custom Exception and ResponseEntity: Create specific exceptions and provide detailed JSON responses for improved clarity.
- Global vs Local Handling: Understand when to use global exception handling (
@ControllerAdvice
) versus local handling (@ExceptionHandler
).
By adopting @ControllerAdvice
, you equip your application with scalable and maintainable exception-handling capabilities. Start implementing it today to build more robust and user-friendly Spring Boot applications!