Real-World Analogy: The Emergency Room Triage Desk
Imagine a hospital where patients arrive with different medical issues (heart attack, broken arm, fever). Instead of having every doctor write their own reception rules, all patients go through a centralized Emergency Triage Desk. The triage desk intercepts the emergency, assigns the severity level, and formats a clear medical status report. @ControllerAdvice is that central triage desk for your Spring Boot REST APIs!
What is @RestControllerAdvice / @ControllerAdvice?
In Spring Boot, @RestControllerAdvice is a specialized interceptor that catches any unhandled exceptions thrown by ANY @RestController across your application. Instead of returning ugly 500 HTML stack traces to clients, it intercepts the error and returns a clean, structured JSON response.
Why is Global Exception Handling Mandatory?
- 🛡️ Prevents Stack Trace Leaks: Prevents raw database SQL errors or internal server code line numbers from being exposed to potential hackers.
- 🎯 Consistent Error Schema: Guarantees that whether a
ResourceNotFoundExceptionor aValidationExceptionoccurs, the frontend receives the exact same JSON format (`timestamp`, `status`, `error`, `message`, `path`). - 🧹 Clean Controllers: Eliminates duplicate
try-catchblocks inside your service and controller methods.
Complete Beginner Code Example:
package com.anujsingh.digitalguru.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import jakarta.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
@RestControllerAdvice // Applies to all @RestController classes in the app
public class GlobalExceptionHandler {
// Catch Custom ResourceNotFoundException
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponseDto> handleNotFound(ResourceNotFoundException ex, HttpServletRequest request) {
ErrorResponseDto error = new ErrorResponseDto(
LocalDateTime.now(),
HttpStatus.NOT_FOUND.value(),
"Resource Not Found",
ex.getMessage(),
request.getRequestURI()
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
// Catch All Other Unexpected Server Exceptions
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponseDto> handleGlobal(Exception ex, HttpServletRequest request) {
ErrorResponseDto error = new ErrorResponseDto(
LocalDateTime.now(),
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"Internal Server Error",
"An unexpected error occurred. Please try again later.",
request.getRequestURI()
);
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
package com.anujsingh.digitalguru.exception;
import java.time.LocalDateTime;
public record ErrorResponseDto(
LocalDateTime timestamp,
int status,
String error,
String message,
String path
) {}
Line-by-Line Breakdown:
@RestControllerAdvice: Combines@ControllerAdviceand@ResponseBodyso returned objects are auto-serialized to JSON.@ExceptionHandler(ResourceNotFoundException.class): Tells Spring: "When aResourceNotFoundExceptionis thrown anywhere, execute this specific method!"ResponseEntity<>(error, HttpStatus.NOT_FOUND): Returns HTTP Status Code404 Not Foundalong with the custom JSON error body.
Common Beginner Mistake
Do not catch exceptions inside your Service layer and return null! Throw descriptive custom exceptions (e.g. throw new ResourceNotFoundException("User not found with id: " + id)) and let @RestControllerAdvice handle the HTTP response!