To implement global exception handling in a Spring application, the @ControllerAdvice annotation is used. It allows you to centralize exception handling across multiple controllers.
Below is an example of how you can implement global exception handling with @ControllerAdvice in your application:
1. Define a Global Exception Handler
Create a class with the @ControllerAdvice annotation to handle exceptions globally. Within this class, use the @ExceptionHandler annotation on methods to define specific exception handling logic.
package org.kodejava;
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;
// Marks this class as a global exception handler
@ControllerAdvice
public class GlobalExceptionHandler {
// Handle specific exceptions
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<?> handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
// Handle global exceptions (for all other exceptions)
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleGlobalException(Exception ex, WebRequest request) {
ErrorResponse errorResponse = new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"An unexpected error occurred",
request.getDescription(false));
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
2. Create a Custom Exception Class (Optional)
Define specific exception classes for your business use cases. For example, a ResourceNotFoundException for handling “not found” errors.
package org.kodejava;
public class ResourceNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message) {
super(message);
}
}
3. Create an Error Response Model
Create a class to structure the error response data consistently.
package org.kodejava;
public class ErrorResponse {
private int statusCode;
private String message;
private String details;
public ErrorResponse(int statusCode, String message, String details) {
this.statusCode = statusCode;
this.message = message;
this.details = details;
}
// Getters and Setters
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
}
4. Throw Custom Exceptions in Your Controller
You can now throw the ResourceNotFoundException or other exceptions in your controllers, and let the global exception handler process them.
package org.kodejava;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class TestController {
@GetMapping("/resource")
public String getResource() {
throw new ResourceNotFoundException("Resource not found with ID");
}
}
5. Test the Application
When you access the /api/resource endpoint, the global exception handler will catch the ResourceNotFoundException and return a structured error response. Example response:
{
"statusCode": 404,
"message": "Resource not found with ID",
"details": "uri=/api/resource"
}
Advantages of Using @ControllerAdvice
- Centralized Exception Handling: Removes the need to write exception handling in multiple controllers.
- Improved Readability: Controllers are cleaner as they no longer handle exceptions.
- Reusability: Reuse the exception handler for different types of exceptions across the application.
- Custom Responses: Provides flexibility to return consistent error responses.
This implementation ensures your application has a robust and maintainable error-handling mechanism using Spring’s @ControllerAdvice.
