Module: Exception Handling

@ExceptionHandler

Exception Handling in Spring Boot: @ExceptionHandler

So far, we've discussed basic exception handling in Spring Boot – letting exceptions propagate and being caught by a global exception handler. But what if you want more fine-grained control, handling specific exceptions within a particular controller? That's where @ExceptionHandler comes in.

What is @ExceptionHandler?

@ExceptionHandler is a Spring annotation that allows you to define methods within a controller to handle exceptions that occur within that controller. It provides a way to centralize exception handling logic for a specific controller, making your code cleaner and more maintainable.

How does it work?

  1. Annotation Placement: You annotate a method within a controller class with @ExceptionHandler.
  2. Exception Type: The method's parameter(s) specify the type of exception(s) it handles. You can handle a single exception type or multiple types.
  3. Handling Logic: The method body contains the logic to handle the exception. This might involve logging the error, returning a custom error response, or redirecting the user.
  4. Method Signature: The method signature must match the exception type you're handling. It can also include other parameters, like the request object.

Example

Let's illustrate with a simple example. Assume we have a ProductController and we want to handle ProductNotFoundException specifically within that controller.

package com.example.demo.controller;

import com.example.demo.exception.ProductNotFoundException;
import com.example.demo.model.Product;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/products")
public class ProductController {

    // ... (other controller methods) ...

    @GetMapping("/{id}")
    public ResponseEntity<Product> getProduct(@PathVariable Long id) {
        if (id == 1) {
            throw new ProductNotFoundException("Product with ID 1 not found");
        }
        return ResponseEntity.ok(new Product(id, "Example Product"));
    }

    @ExceptionHandler(ProductNotFoundException.class)
    public ResponseEntity<String> handleProductNotFoundException(ProductNotFoundException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }
}

Explanation:

  • @ExceptionHandler(ProductNotFoundException.class): This annotation indicates that the handleProductNotFoundException method will handle ProductNotFoundException exceptions.
  • handleProductNotFoundException(ProductNotFoundException ex): This method takes a ProductNotFoundException object as a parameter. Spring automatically passes the exception instance to this method when a ProductNotFoundException is thrown within the controller.
  • return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);: This returns a ResponseEntity with the exception message and a NOT_FOUND status code.

Testing:

If you now make a request to /products/1, the getProduct method will throw a ProductNotFoundException. Spring will intercept this exception and call the handleProductNotFoundException method, which will return a 404 Not Found response with the message "Product with ID 1 not found".

Handling Multiple Exception Types

You can handle multiple exception types within a single @ExceptionHandler method by listing them in the annotation:

@ExceptionHandler({ProductNotFoundException.class, IllegalArgumentException.class})
public ResponseEntity<String> handleExceptions(Exception ex) {
    // Common handling logic for both exceptions
    return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}

In this case, the method will handle both ProductNotFoundException and IllegalArgumentException. Note that the parameter type is Exception to accommodate both.

Using @ControllerAdvice with @ExceptionHandler

While @ExceptionHandler is defined within a controller, you can also use @ControllerAdvice to define exception handling methods that apply to all controllers in your application. This is a powerful way to create centralized exception handling logic. We'll cover @ControllerAdvice in more detail in the next section.

Benefits of Using @ExceptionHandler

  • Controller-Specific Handling: Allows you to handle exceptions in a way that's specific to the logic within a particular controller.
  • Clean Code: Keeps exception handling logic close to the code that might throw the exception, improving readability and maintainability.
  • Flexibility: Provides fine-grained control over how exceptions are handled.
  • Custom Error Responses: Enables you to return custom error responses tailored to the specific exception and controller.

When to use @ExceptionHandler vs. Global Exception Handling

  • @ExceptionHandler: Use when you need to handle exceptions in a controller-specific way. For example, if a controller has unique validation rules or error handling requirements.
  • Global Exception Handling (using @ControllerAdvice): Use for common exception handling logic that applies to all controllers. For example, logging exceptions, returning generic error responses, or handling unexpected errors. You can also combine both approaches – using global exception handling for general cases and @ExceptionHandler for specific controller needs.

This provides a solid foundation for understanding and using @ExceptionHandler in your Spring Boot applications. Remember to tailor your exception handling to the specific needs of your application for a robust and user-friendly experience.