Monday, 16 March 2015

Handle exceptions in spring boot globally

In Spring Boot, global exception handling is typically done using @ControllerAdvice and @ExceptionHandler annotations. This approach helps you centralize exception handling logic for all controllers in your application, promoting cleaner code and consistent error responses.

✅ Steps to Handle Exceptions Globally in Spring Boot


1. Create a Custom Exception (optional)

If your application has specific business rules, define custom exceptions:

package com.example.demo.exception; public class ResourceNotFoundException extends RuntimeException { public ResourceNotFoundException(String message) { super(message); } }

2. Create a Global Exception Handler Class

Use @ControllerAdvice to handle exceptions across all controllers.


package com.example.demo.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; @ControllerAdvice public class GlobalExceptionHandler { // Handle custom exceptions @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<Object> handleResourceNotFound(ResourceNotFoundException ex) { Map<String, Object> error = new HashMap<>(); error.put("timestamp", LocalDateTime.now()); error.put("message", ex.getMessage()); error.put("status", HttpStatus.NOT_FOUND.value()); return new ResponseEntity<>(error, HttpStatus.NOT_FOUND); } // Handle validation errors @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<Object> handleValidationErrors(MethodArgumentNotValidException ex) { Map<String, Object> error = new HashMap<>(); Map<String, String> fieldErrors = new HashMap<>(); ex.getBindingResult().getFieldErrors().forEach(err -> { fieldErrors.put(err.getField(), err.getDefaultMessage()); }); error.put("timestamp", LocalDateTime.now()); error.put("status", HttpStatus.BAD_REQUEST.value()); error.put("message", "Validation Failed"); error.put("errors", fieldErrors); return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST); } // Handle generic exceptions @ExceptionHandler(Exception.class) public ResponseEntity<Object> handleGenericException(Exception ex) { Map<String, Object> error = new HashMap<>(); error.put("timestamp", LocalDateTime.now()); error.put("message", "An unexpected error occurred"); error.put("details", ex.getMessage()); error.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value()); return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR); } }

✅ Optional: Create a Standard Error Response DTO

package com.example.demo.exception; import java.time.LocalDateTime; public class ErrorResponse { private LocalDateTime timestamp; private int status; private String message; private Object details; // Constructors, getters, and setters }

Then, return ErrorResponse objects instead of Map<String, Object> in the handler.


🧪 Example: When a Department is Not Found

If a controller throws:


throw new ResourceNotFoundException("Department not found with ID: " + id);

Then you’ll get a JSON response like:

{ "timestamp": "2025-07-27T11:35:00", "message": "Department not found with ID: 5", "status": 404 }

✅ Summary

ComponentPurpose
@ControllerAdviceDeclares a global exception handler class
@ExceptionHandlerHandles specific exception types
@ResponseStatus (opt.)Set HTTP status code for exceptions
MethodArgumentNotValidExceptionHandles validation errors (e.g. @Valid)