Monday, December 8, 2025

thumbnail

Exception Handling in Spring Boot

 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

Get Directions

Subscribe by Email

Follow Updates Articles from This Blog via Email

No Comments

About

Search This Blog

Powered by Blogger.

Blog Archive