Introduction
In a Spring Boot application, handling exceptions gracefully is crucial for providing meaningful error responses to clients. Instead of writing exception handling logic in each controller, we can use @ControllerAdvice
to centralize exception handling across the application.
What is @ControllerAdvice?
@ControllerAdvice
is a specialized component in Spring that allows you to handle exceptions globally across multiple controllers. It helps maintain cleaner code by separating exception handling logic from the main business logic.
Creating a Global Exception Handler using @ControllerAdvice
Step 1: Define Custom Exceptions
Let’s define a couple of custom exceptions:
1 2 3 4 5 6 7 | package com.example.exception; public class ResourceNotFoundException extends RuntimeException { public ResourceNotFoundException(String message) { super (message); } } |
1 2 3 4 5 6 7 | package com.example.exception; public class BadRequestException extends RuntimeException { public BadRequestException(String message) { super (message); } } |
Step 2: Implement the Global Exception Handler
Create a class annotated with @ControllerAdvice
to handle exceptions globally:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | package com.example.advice; import com.example.exception.ResourceNotFoundException; import com.example.exception.BadRequestException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ControllerAdvice; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler (ResourceNotFoundException. class ) public ResponseEntity<Map<String, Object>> handleResourceNotFoundException(ResourceNotFoundException ex) { Map<String, Object> errorDetails = new HashMap<>(); errorDetails.put( "timestamp" , LocalDateTime.now()); errorDetails.put( "message" , ex.getMessage()); return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND); } @ExceptionHandler (BadRequestException. class ) public ResponseEntity<Map<String, Object>> handleBadRequestException(BadRequestException ex) { Map<String, Object> errorDetails = new HashMap<>(); errorDetails.put( "timestamp" , LocalDateTime.now()); errorDetails.put( "message" , ex.getMessage()); return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST); } @ExceptionHandler (Exception. class ) public ResponseEntity<Map<String, Object>> handleGlobalException(Exception ex) { Map<String, Object> errorDetails = new HashMap<>(); errorDetails.put( "timestamp" , LocalDateTime.now()); errorDetails.put( "message" , "An unexpected error occurred" ); return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR); } } |
Step 3: Use Exceptions in a Controller
Create a simple Spring Boot controller that triggers the exceptions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package com.example.controller; import com.example.exception.ResourceNotFoundException; import com.example.exception.BadRequestException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @GetMapping ( "/find" ) public String findResource( @RequestParam (required = false ) String id) { if (id == null ) { throw new BadRequestException( "ID parameter is missing" ); } if ( "123" .equals(id)) { return "Resource found" ; } throw new ResourceNotFoundException( "Resource with ID " + id + " not found" ); } } |
Step 4: Testing the Application
1. Valid Request:
1 2 3 | GET /find?id=123 Response: 200 OK "Resource found" |
2. Missing ID Parameter:
1 2 3 4 5 6 | GET /find Response: 400 Bad Request { "timestamp": "2024-03-18T12:00:00", "message": "ID parameter is missing" } |
3. Invalid ID:
1 2 3 4 5 6 | GET /find?id=999 Response: 404 Not Found { "timestamp": "2024-03-18T12:00:00", "message": "Resource with ID 999 not found" } |
Conclusion
Using @ControllerAdvice
, we can effectively centralize and manage exception handling in Spring Boot applications. This approach enhances code maintainability, improves user experience, and ensures consistent error responses.