Spring Boot @ControllerAdvice Example

1. Introduction

The @ControllerAdvice annotation in Spring Boot is used to handle exceptions across the whole application in one global handling component. It can be used to define @ExceptionHandler, @InitBinder, and @ModelAttribute methods that apply to all @RequestMapping methods.

Key Points:

1. @ControllerAdvice allows for global exception handling, data binding, and model attribute population across all controllers.

2. It is a controller-based class enhancement that applies to all controllers.

3. @ControllerAdvice can target specific packages or even specific annotations for more focused behavior.

2. Implementation Steps

1. Create a new Spring Boot project with web dependencies.

2. Create a @ControllerAdvice annotated class to advise all controllers.

3. Define @ExceptionHandler methods within the @ControllerAdvice class to handle specific exceptions.

4. Optionally, use @ModelAttribute and @InitBinder within @ControllerAdvice to apply global configurations.

5. Test the application to verify that the global advice is being applied.

3. Implementation Example

// Step 2: Create a @ControllerAdvice class
@ControllerAdvice
public class GlobalControllerAdvice {

    // Step 3: Handle a custom exception globally
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<String> handleCustomException(CustomException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }

    // Step 4: Use @ModelAttribute at the global level
    @ModelAttribute("globalAttribute")
    public String addGlobalAttribute() {
        return "Global Data";
    }
}

// Step 3: Custom exception for demonstration purposes
class CustomException extends RuntimeException {
    public CustomException(String message) {
        super(message);
    }
}

// A sample controller to demonstrate exception handling
@RestController
public class SampleController {
    @GetMapping("/test")
    public String testException() {
        throw new CustomException("Test Exception");
    }
}

// Step 5: Main application class to start Spring Boot application
@SpringBootApplication
public class ControllerAdviceExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ControllerAdviceExampleApplication.class, args);
    }
}

// Make a request to /test to trigger the CustomException.

Output:

// Making a request to /test endpoint:
Status: 400 BAD REQUEST
Body: Test Exception

Explanation:

1. GlobalControllerAdvice is marked with @ControllerAdvice indicating it contains global configurations for all controllers.

2. @ExceptionHandler(CustomException.class) method handleCustomException provides a global exception handling mechanism for CustomException.

3. @ModelAttribute("globalAttribute") defines a global model attribute that is available to all controllers.

4. SampleController contains a method testException which simulates an endpoint throwing CustomException.

5. When the /test endpoint is hit, CustomException is thrown and caught by the global exception handler handleCustomException.

6. The application returns a 400 Bad Request status code with the message "Test Exception", indicating the @ControllerAdvice is in effect.

4. Handle Specific and Global Exceptions using @ControllerAdvice and @ExceptionHandler

1. @ExceptionHandler can be used for specific exception types or for a general exception-handling strategy.

2. When used within a controller, it only handles exceptions for that controller. When used within a @ControllerAdvice class, it can handle exceptions globally.

3. @ExceptionHandler methods can return any kind of response entity or view, allowing for flexible error responses.

Implementation Steps

1. Define a custom exception class.

2. Use @ExceptionHandler within a controller to handle specific exceptions.

3. Create a @ControllerAdvice class with an @ExceptionHandler to handle global exceptions.

4. Test both specific and global exception handling through HTTP requests.

Implementation Example

// Step 1: Define a custom exception class
class ResourceNotFoundException extends RuntimeException {
    ResourceNotFoundException(String message) {
        super(message);
    }
}

// Custom exception for other generic errors
class GenericErrorException extends RuntimeException {
    GenericErrorException(String message) {
        super(message);
    }
}

// Step 2: Controller with a specific exception handler
@RestController
@RequestMapping("/api")
public class SampleController {

    // Endpoint that may throw ResourceNotFoundException
    @GetMapping("/resource")
    public ResponseEntity<String> getResource() {
        // Simulate an error condition
        throw new ResourceNotFoundException("Resource not found");
    }

    // Controller-specific exception handler
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<String> handleResourceNotFound(ResourceNotFoundException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }
}

// Step 3: Global exception handling with @ControllerAdvice
@ControllerAdvice
public class GlobalExceptionHandler {

    // Global exception handler for all other exceptions
    @ExceptionHandler(GenericErrorException.class)
    public ResponseEntity<String> handleGenericError(GenericErrorException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    // Catch-all exception handler
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleAll(Exception ex) {
        return new ResponseEntity<>("Global error handling: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

// Step 4: Main class to run the Spring Boot application
@SpringBootApplication
public class ExceptionHandlerExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ExceptionHandlerExampleApplication.class, args);
    }
}

// Test the application by making requests to /api/resource to trigger ResourceNotFoundException and any other endpoint for GenericErrorException or other exceptions.

Output:

// When accessing /api/resource and ResourceNotFoundException is thrown:
Status: 404 NOT FOUND
Body: Resource not found
// When GenericErrorException or any other exception is thrown from other endpoints:
Status: 500 INTERNAL SERVER ERROR
Body: Global error handling: <error message here>

Explanation:

1. ResourceNotFoundException and GenericErrorException are custom exceptions used to simulate specific error conditions.

2. SampleController is a REST controller that has an endpoint that could throw ResourceNotFoundException.

3. The method handleResourceNotFound within SampleController uses @ExceptionHandler to catch ResourceNotFoundException specifically for this controller.

4. GlobalExceptionHandler marked with @ControllerAdvice is the global error handler that catches GenericErrorException and other exceptions across the entire application.

5. handleGenericError in GlobalExceptionHandler handles GenericErrorException specifically, while handleAll provides a catch-all solution for any unhandled exceptions.

6. ExceptionHandlerExampleApplication contains the main method and serves as the entry point for running the Spring Boot application.

7. The outputs indicate the HTTP status and response body for different exceptions, demonstrating specific and global exception handling in action.


Comments