DIGITAL GURU
Java Roadmap Portfolio

Global Exception Handling (@ControllerAdvice)

Catching exceptions across all REST controllers globally to produce uniform error JSONs.

Anuj Kumar Singh Written by Anuj Kumar Singh (Lead Engineer, 13+ yrs exp) 5 min read Verified Spring Boot 3+ Guide
3D Architecture Visualizer

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 ResourceNotFoundException or a ValidationException occurs, the frontend receives the exact same JSON format (`timestamp`, `status`, `error`, `message`, `path`).
  • 🧹 Clean Controllers: Eliminates duplicate try-catch blocks inside your service and controller methods.

Complete Beginner Code Example:

GlobalExceptionHandler.java
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);
    }
}
ErrorResponseDto.java
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:

  1. @RestControllerAdvice: Combines @ControllerAdvice and @ResponseBody so returned objects are auto-serialized to JSON.
  2. @ExceptionHandler(ResourceNotFoundException.class): Tells Spring: "When a ResourceNotFoundException is thrown anywhere, execute this specific method!"
  3. ResponseEntity<>(error, HttpStatus.NOT_FOUND): Returns HTTP Status Code 404 Not Found along 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!