Discover the Best Way to Handle Exceptions in Spring Boot
Centralized Exception Handling in Spring with @ControllerAdvice
Exception handling is an important aspect of application development, especially in production environments where stable, predictable error responses are essential.
In Spring applications, using @ControllerAdvice
allows you to centralize and streamline exception handling, enabling a consistent and easily maintainable error-handling approach across your application.
I learned to do this while working on a payment infrastructure, and it quickly became one of my favorite setups for new projects.
Why Use @ControllerAdvice
?
@ControllerAdvice
in Spring is a powerful feature that enables centralized error handling, applying to multiple controllers throughout the application.
Without it, each controller would need its own error-handling logic, leading to repetitive code and inconsistent responses. By implementing @ControllerAdvice
, you can:
Centralize Error Handling: Define error-handling logic in one place and apply it across all controllers.
Ensure Consistent Responses: Standardize error responses to help client applications handle errors uniformly.
Simplify Controller Code: Keep controllers focused on handling requests, moving error-handling logic out for cleaner code.
Getting Started with @ControllerAdvice
Here’s how you can set up @ControllerAdvice
in a Spring Boot application to manage exceptions effectively. We’ll cover:
Setting up
@ControllerAdvice
to handle specific exceptions.Creating a global exception handler for unexpected errors.
Customizing the structure of error responses.
Step 1: Define a Custom Exception
To show exception handling, let’s create a custom exception called ResourceNotFoundException
.
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
Step 2: Create a Global Exception Handler with @ControllerAdvice
Now, we’ll define a global exception handler class using @ControllerAdvice
. This class will handle both specific and general exceptions in a centralized way.
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalExceptionHandler {
// Handle specific exception
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<Object> handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
Map<String, Object> body = new HashMap<>();
body.put("status", HttpStatus.NOT_FOUND.value());
body.put("error", "Resource Not Found");
body.put("message", ex.getMessage());
body.put("timestamp", System.currentTimeMillis());
return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
}
// Handle other exceptions
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleGeneralException(Exception ex, WebRequest request) {
Map<String, Object> body = new HashMap<>();
body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
body.put("error", "Internal Server Error");
body.put("message", "An unexpected error occurred");
body.put("timestamp", System.currentTimeMillis());
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Here:
@ControllerAdvice
makes this class a global exception handler.@ExceptionHandler
defines methods that handle specific exceptions. In this case:handleResourceNotFoundException
handlesResourceNotFoundException
with a structured JSON response containing details such as status, error, message, and timestamp.handleGeneralException
provides a generic error response for unexpected errors.
Step 3: Customize Error Responses with a Bean
To further customize responses, we can use an ErrorBean
class.
@AllArgsConstructor
public class ErrorBean {
private String message;
private Boolean success;
}
Now, we can update the handleGeneralException
method to use ErrorBean
:
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorBean> handleGeneralException(Exception ex, WebRequest request) {
ErrorBean message = new ErrorBean(ex.getMessage(), false);
return new ResponseEntity<>(message, HttpStatus.INTERNAL_SERVER_ERROR);
}
Step 4: Create Custom Exception Classes for Additional Scenarios
If your application has specific error cases, you can create custom exception classes.
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class MerchantNotExistException extends RuntimeException {
private static final long serialVersionUID = 1L;
public MerchantNotExistException(String exception) {
super(exception);
}
}
By centralizing exception handling in Spring with @ControllerAdvice
, you simplify error management and promote consistency across your application, making your code cleaner and easier to maintain. Whether for a small project or a production-scale system, this approach helps ensure robust and predictable error handling.