Spring Boot @ExceptionHandler Example

1. Introduction

The @ExceptionHandler annotation in Spring Boot is essential for centralized exception handling within a controller. It can be used to handle a single specific exception or multiple types of exceptions. This simplifies error handling by avoiding boilerplate code scattered throughout the service layers.

Key Points:

1. @ExceptionHandler allows you to handle specific exceptions and customize the response directly within your controller.

2. It can be used to handle exceptions globally across the application or within a specific controller.

3. When an exception is thrown, the annotated method is invoked and it can return any type of response body, such as an error message or a status code.

4. Multiple exceptions can be handled by the same @ExceptionHandler method by passing an array of exception classes to the annotation.

2. Implementation Steps

1. Define a controller in your Spring Boot application.

2. Create a custom exception class that you want to handle.

3. Within the controller, create a method and annotate it with @ExceptionHandler, specifying the exception class.

4. Implement the logic for handling the exception within the method.

5. Throw the custom exception from one of the controller methods to trigger the exception handler.

6. Test the controller to ensure that the exception is being handled as expected.

3. Implementation Example

// Step 2: Create a custom exception class
class CustomException extends RuntimeException {
    CustomException(String message) {
        super(message);
    }
}

// Step 1: Define a controller
@RestController
public class SampleController {

    // Step 5: Controller method that throws the custom exception
    @GetMapping("/exception")
    public String throwException() {
        throw new CustomException("A custom exception occurred");
    }

    // Step 3: Method to handle the custom exception
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<String> handleCustomException(CustomException ex) {
        // Step 4: Return the custom response for the exception
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }
}

// Step 6: Define the main application class
@SpringBootApplication
public class ExceptionHandlerExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ExceptionHandlerExampleApplication.class, args);
    }
}

Output:

A custom exception occurred

Explanation:

1. The custom exception class CustomException is defined to be thrown when something goes wrong within a controller method.

2. SampleController is a @RestController that contains an endpoint mapped to /exception which deliberately throws CustomException.

3. handleCustomException method is annotated with @ExceptionHandler(CustomException.class) indicating it will handle exceptions of type CustomException.

4. When CustomException is thrown, the handleCustomException method builds and returns a ResponseEntity with a custom message and HTTP status code.

5. ExceptionHandlerExampleApplication contains the main method to start the application.

6. The output is the string "A custom exception occurred", which is the message from the CustomException caught and handled by the handleCustomException method.

4. Handle Multiple Exceptions using @ExceptionHandler Example

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

class InvalidInputException extends RuntimeException {
    InvalidInputException(String message) {
        super(message);
    }
}

// Step 1: Create a web controller
@RestController
public class ExceptionHandlingController {

    // Step 5: Method that may throw ResourceNotFoundException
    @GetMapping("/resource/{id}")
    public String findResource(@PathVariable String id) {
        throw new ResourceNotFoundException("Resource with ID " + id + " not found.");
    }

    // Method that may throw InvalidInputException
    @PostMapping("/resource")
    public String createResource(@RequestBody String content) {
        if (content.isEmpty()) {
            throw new InvalidInputException("Invalid input provided.");
        }
        return "Resource created";
    }

    // Step 3: Single method to handle multiple types of exceptions
    @ExceptionHandler({ResourceNotFoundException.class, InvalidInputException.class})
    public ResponseEntity<String> handleExceptions(Exception ex) {
        // Step 4: Implement custom response logic
        HttpStatus status = ex instanceof ResourceNotFoundException ?
            HttpStatus.NOT_FOUND : HttpStatus.BAD_REQUEST;
        return new ResponseEntity<>(ex.getMessage(), status);
    }
}

// Step 6: Define the main application class
@SpringBootApplication
public class ExceptionHandlerExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(ExceptionHandlerExampleApplication.class, args);
    }
}

Output:

// When /resource/42 is accessed:
Status: 404 Not Found
Body: Resource with ID 42 not found.
// When /resource is accessed with an empty content:
Status: 400 Bad Request
Body: Invalid input provided.

Explanation:

1. ExceptionHandlingController is a @RestController with two endpoints that throw ResourceNotFoundException and InvalidInputException, respectively.

2. @ExceptionHandler on the handleExceptions method is set up to catch both ResourceNotFoundException and InvalidInputException.

3. When an exception occurs, handleExceptions checks the type of exception and assigns an appropriate HttpStatus.

4. The handleExceptions method then returns a ResponseEntity with the error message and the determined HttpStatus.

5. ExceptionHandlerExampleApplication is the entry point of the application, bootstrapping the Spring Boot app.

6. Depending on which endpoint is hit and what exception is thrown, the handleExceptions method will return a corresponding HTTP status code along with the exception's message as the response body.


Comments