Handling Exceptions in Spring Boot with @ControllerAdvice

1 min read

Handling Exceptions in Spring Boot with @ControllerAdvice

Introduction

In a Spring Boot application, handling exceptions gracefully is crucial for providing meaningful error responses to clients. Instead of writing exception handling logic in each controller, we can use @ControllerAdvice to centralize exception handling across the application.

What is @ControllerAdvice?

@ControllerAdvice is a specialized component in Spring that allows you to handle exceptions globally across multiple controllers. It helps maintain cleaner code by separating exception handling logic from the main business logic.

Creating a Global Exception Handler using @ControllerAdvice

Step 1: Define Custom Exceptions

Let’s define a couple of custom exceptions:

package com.example.exception;

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

public class BadRequestException extends RuntimeException {
    public BadRequestException(String message) {
        super(message);
    }
}

Step 2: Implement the Global Exception Handler

Create a class annotated with @ControllerAdvice to handle exceptions globally:

package com.example.advice;

import com.example.exception.ResourceNotFoundException;
import com.example.exception.BadRequestException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<Map<String, Object>> handleResourceNotFoundException(ResourceNotFoundException ex) {
        Map<String, Object> errorDetails = new HashMap<>();
        errorDetails.put("timestamp", LocalDateTime.now());
        errorDetails.put("message", ex.getMessage());
        return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(BadRequestException.class)
    public ResponseEntity<Map<String, Object>> handleBadRequestException(BadRequestException ex) {
        Map<String, Object> errorDetails = new HashMap<>();
        errorDetails.put("timestamp", LocalDateTime.now());
        errorDetails.put("message", ex.getMessage());
        return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, Object>> handleGlobalException(Exception ex) {
        Map<String, Object> errorDetails = new HashMap<>();
        errorDetails.put("timestamp", LocalDateTime.now());
        errorDetails.put("message", "An unexpected error occurred");
        return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

Step 3: Use Exceptions in a Controller

Create a simple Spring Boot controller that triggers the exceptions:

package com.example.controller;

import com.example.exception.ResourceNotFoundException;
import com.example.exception.BadRequestException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @GetMapping("/find")
    public String findResource(@RequestParam(required = false) String id) {
        if (id == null) {
            throw new BadRequestException("ID parameter is missing");
        }
        if ("123".equals(id)) {
            return "Resource found";
        }
        throw new ResourceNotFoundException("Resource with ID " + id + " not found");
    }
}

Step 4: Testing the Application

1. Valid Request:

GET /find?id=123
Response: 200 OK
"Resource found"

2. Missing ID Parameter:

GET /find
Response: 400 Bad Request
{
    "timestamp": "2024-03-18T12:00:00",
    "message": "ID parameter is missing"
}

3. Invalid ID:

GET /find?id=999
Response: 404 Not Found
{
    "timestamp": "2024-03-18T12:00:00",
    "message": "Resource with ID 999 not found"
}

Conclusion

Using @ControllerAdvice, we can effectively centralize and manage exception handling in Spring Boot applications. This approach enhances code maintainability, improves user experience, and ensures consistent error responses.

🤞 Never miss a story from us, get weekly updates to your inbox!