Here’s a comprehensive guide on Exception Handling in Spring Boot, covering the key concepts, annotations, and best practices for building robust, maintainable REST APIs.
Spring Boot provides multiple mechanisms to handle exceptions globally or locally, ensuring consistent responses and cleaner controller code.
1️⃣ Basics of Exception Handling in Spring Boot
In Spring Boot, you can handle exceptions:
Locally – Within a specific controller using try-catch or @ExceptionHandler.
Globally – Across all controllers using @ControllerAdvice or @RestControllerAdvice.
Why use centralized exception handling?
Consistent error responses
Less repetitive code
Easier maintenance
Better logging and monitoring
2️⃣ Using @ExceptionHandler for Local Controller Handling
You can handle exceptions within a specific controller:
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
if (id <= 0) {
throw new IllegalArgumentException("User ID must be positive");
}
return new User(id, "John Doe");
}
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<String> handleIllegalArgument(IllegalArgumentException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
✅ Explanation:
@ExceptionHandler tells Spring Boot which exceptions to catch.
The method can return ResponseEntity, JSON, or a custom error object.
Local handling affects only this controller.
3️⃣ Global Exception Handling with @ControllerAdvice
For global handling across multiple controllers, use @ControllerAdvice (or @RestControllerAdvice for REST APIs).
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<Map<String, Object>> handleIllegalArgument(IllegalArgumentException ex) {
Map<String, Object> errorBody = new HashMap<>();
errorBody.put("timestamp", LocalDateTime.now());
errorBody.put("status", HttpStatus.BAD_REQUEST.value());
errorBody.put("error", "Bad Request");
errorBody.put("message", ex.getMessage());
return new ResponseEntity<>(errorBody, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleAllExceptions(Exception ex) {
Map<String, Object> errorBody = new HashMap<>();
errorBody.put("timestamp", LocalDateTime.now());
errorBody.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
errorBody.put("error", "Internal Server Error");
errorBody.put("message", ex.getMessage());
return new ResponseEntity<>(errorBody, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
✅ Explanation:
@RestControllerAdvice applies to all controllers globally.
You can define multiple @ExceptionHandler methods for different exception types.
Provides a centralized place for logging and building error responses.
4️⃣ Returning Custom Error Response
It's good practice to create a custom error response object:
public class ErrorResponse {
private LocalDateTime timestamp;
private int status;
private String error;
private String message;
public ErrorResponse(LocalDateTime timestamp, int status, String error, String message) {
this.timestamp = timestamp;
this.status = status;
this.error = error;
this.message = message;
}
// Getters and Setters
}
Use it in the global handler:
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
LocalDateTime.now(),
HttpStatus.NOT_FOUND.value(),
"Not Found",
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
✅ Advantages:
Clear, structured JSON response
Easier for front-end to parse
Consistent format across all endpoints
5️⃣ Handling Validation Errors (@Valid)
Spring Boot automatically throws MethodArgumentNotValidException for invalid inputs.
Example DTO with validation:
public class UserDTO {
@NotBlank(message = "Name is required")
private String name;
@Email(message = "Invalid email format")
private String email;
// Getters and setters
}
Controller:
@PostMapping("/users")
public User createUser(@Valid @RequestBody UserDTO user) {
return new User(1L, user.getName());
}
Global exception handler for validation errors:
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationErrors(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
6️⃣ Common HTTP Status Exceptions
Spring Boot provides built-in exception classes you can throw:
Exception HTTP Status Description
ResponseStatusException Any Generic exception with custom status
HttpClientErrorException 4xx Client-side errors
HttpServerErrorException 5xx Server-side errors
NoHandlerFoundException 404 Endpoint not found
Example with ResponseStatusException:
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
if (id <= 0) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "ID must be positive");
}
return new User(id, "John Doe");
}
7️⃣ Best Practices for Exception Handling in Spring Boot
Use @RestControllerAdvice for centralized handling.
Return structured error responses with timestamp, status, and message.
Create custom exceptions for domain-specific errors (e.g., ResourceNotFoundException).
Handle validation exceptions separately.
Avoid using try-catch in controllers unless necessary.
Log exceptions for monitoring and debugging.
8️⃣ Summary Flow
Controller -> throws Exception -> @RestControllerAdvice -> @ExceptionHandler -
Learn Full Stack JAVA Course in Hyderabad
Read More
Spring Boot Annotations You Must Know
Dependency Injection in Spring Framework
Creating REST APIs using Spring Boot
Spring Boot vs Spring MVC – Key Differences
Visit Our Quality Thought Institute in Hyderabad
Subscribe by Email
Follow Updates Articles from This Blog via Email
No Comments