Spring Boot – Exception Handling
Spring Boot is built on the top of the spring and contains all the features of spring. And is becoming a favorite of developers these days because of its rapid production-ready environment which enables the developers to directly focus on the logic instead of struggling with the configuration and setup. Spring Boot is a microservice-based framework and making a production-ready application in it takes very little time. Exception Handling in Spring Boot helps to deal with errors and exceptions present in APIs so as to deliver a robust enterprise application. This article covers various ways in which exceptions can be handled in a Spring Boot Project. Let’s do the initial setup to explore each approach in more depth.
In order to create a simple spring boot project using Spring Initializer, please refer to this article. Now let’s develop a Spring Boot Restful Webservice that performs CRUD operations on Customer Entity. We will be using MYSQL database for storing all necessary data.
Step 1: Creating a JPA Entity class Customer with three fields id, name, and address.
The Customer class is annotated with @Entity annotation and defines getters, setters, and constructors for the fields.
Step 2: Creating a CustomerRepository Interface
The CustomerRepository interface is annotated with @Repository annotation and extends the JpaRepository of Spring Data JPA.
Step 3: Creating Custom made Exceptions that can be thrown during necessary scenarios while performing CRUD.
CustomerAlreadyExistsException: This exception can be thrown when the user tries to add a customer that already exists in the database.
NoSuchCustomerExistsException: This exception can be thrown when the user tries to delete or update a customer record that doesn’t exist in the database.
Note: Both Custom Exception classes extend RuntimeException.
Step 4: Creating interface CustomerService and implementing class CustomerServiceImpl of service layer.
The CustomerService interface defines three different methods:
- Customer getCustomer(Long id): To get a customer record by its id. This method throws a NoSuchElementException exception when it doesn’t find a customer record with the given id.
- String addCustomer(Customer customer): To add details of a new Customer to the database. This method throws a CustomerAlreadyExistsException exception when the user tries to add a customer that already exists.
- String updateCustomer(Customer customer): To update details of Already existing Customers. This method throws a NoSuchCustomerExistsException exception when the user tries to update details of a customer that doesn’t exist in the database.
The Interface and service implementation class is as follows:
Step 5: Creating Rest Controller CustomerController which defines various APIs.
Now let’s go through the various ways in which we can handle the Exceptions thrown in this project.
Default Exception Handling by Spring Boot:
The getCustomer() method defined by CustomerController is used to get a customer with a given Id. It throws a NoSuchElementException when it doesn’t find a Customer record with the given id. On Running the Spring Boot Application and hitting the /getCustomer API with an Invalid Customer Id, we get a NoSuchElementException completely handled by Spring Boot as follows:
Spring Boot provides a systematic error response to the user with information such as timestamp, HTTP status code, error, message, and the path.
Using Spring Boot @ExceptionHandler Annotation:
@ExceptionHandler annotation provided by Spring Boot can be used to handle exceptions in particular Handler classes or Handler methods. Any method annotated with this is automatically recognized by Spring Configuration as an Exception Handler Method. An Exception Handler method handles all exceptions and their subclasses passed in the argument. It can also be configured to return a specific error response to the user. So let’s create a custom ErrorResponse class so that the exception is conveyed to the user in a clear and concise way as follows:
The addCustomer() method defined by CustomerController throws a CustomerAlreadyExistsException when the user tries to add a Customer that already exists in the database else it saves the customer details.
To handle this exception let’s define a handler method handleCustomerAlreadyExistsException() in the CustomerController.So now when addCustomer() throws a CustomerAlreadyExistsException, the handler method gets invoked which returns a proper ErrorResponse to the user.
Note: Spring Boot allows to annotate a method with @ResponseStatus to return the required Http Status Code.
On Running the Spring Boot Application and hitting the /addCustomer API with an existing Customer, CustomerAlreadyExistsException gets completely handled by handler method as follows:
Using @ControllerAdvice for Global Exception Handler:
In the previous approach, we can see that the @ExceptionHandler annotated method can only handle the exceptions thrown by that particular class. However, if we want to handle any exception thrown throughout the application we can define a global exception handler class and annotate it with @ControllerAdvice.This annotation helps to integrate multiple exception handlers into a single global unit.
The updateCustomer() method defined in CustomerController throws a NoSuchCustomerExistsException exception if the user tries to update details of a customer that doesn’t already exist in the database else it successfully saves the updated details for that particular customer.
To handle this exception, let’s define a GlobalExceptionHandler class annotated with @ControllerAdvice. This class defines the ExceptionHandler method for NoSuchCustomerExistsException exception as follows.
On Running the Spring Boot Application and hitting the /updateCustomer API with invalid Customer details, NoSuchCustomerExistsException gets thrown which is completely handled by the handler method defined in GlobalExceptionHandler class as follows: