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?
- Annotation Placement: You annotate a method within a controller class with
@ExceptionHandler. - 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.
- 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.
- 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 thehandleProductNotFoundExceptionmethod will handleProductNotFoundExceptionexceptions.handleProductNotFoundException(ProductNotFoundException ex): This method takes aProductNotFoundExceptionobject as a parameter. Spring automatically passes the exception instance to this method when aProductNotFoundExceptionis thrown within the controller.return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);: This returns aResponseEntitywith the exception message and aNOT_FOUNDstatus 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@ExceptionHandlerfor 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.