In this tutorial, we will show you how to handle exceptions in Spring Boot REST API application.

Spring Boot Exception Handling:

This guide helps you to understand how to define generic/global exception handlers in spring boot application. Here I am going to implement a complete Spring boot restful service with all the CRUD operations and handling proper exception handling.

Technologies:

  • Spring Boot Strater 2.0.4 RELEASE
  • Spring Boot Starter WEB
  • Spring Boot Starter JDBC
  • MySql Connector
  • Java 1.8

Application Structure:

Spring Boot Exception Handling Example

Dependencies:

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.onlinetutorialspoint</groupId>
  <artifactId>Spring-Boot-ExceptionHandling</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Spring-Boot-ExceptionHandling</name>
  <description>Spring Boot Exception Handling Example</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

Creating a Model class:

Item.java
package com.onlinetutorialspoint.model;

public class Item {
    private int id;
    private String name;
    private String category;

    public Item() {
    }

    public Item(int id, String name, String category) {
        this.id = id;
        this.name = name;
        this.category = category;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }
}

Preparing Spring Boot Error Response classes. This is the main response class representing response status, error message, and timestamp.

ItemErrorResponse.java
package com.onlinetutorialspoint.exception;

public class ItemErrorResponse {

    private int status;

    public ItemErrorResponse() {
    }

    private String message;
    private  long timeStamp;

    public ItemErrorResponse(int status, String message, long timeStamp) {
        this.status = status;
        this.message = message;
        this.timeStamp = timeStamp;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public long getTimeStamp() {
        return timeStamp;
    }

    public void setTimeStamp(long timeStamp) {
        this.timeStamp = timeStamp;
    }
}

Create ItemNotFoundException class, responsible for handling scenarios like when an item is not available in our database.

ItemNotFoundException.java
package com.onlinetutorialspoint.exception;

public class ItemNotFoundException extends RuntimeException{

    public ItemNotFoundException(String message) {
        super(message);
    }

    public ItemNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public ItemNotFoundException(Throwable cause) {
        super(cause);
    }
}

Create a global ItemExceptionHandler, which handles ItemNotFoundException and all types of exceptions like 403, 500 and so on.

ItemExceptionHandler.java
package com.onlinetutorialspoint.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class ItemExceptionHandler {

    @ExceptionHandler
    public ResponseEntity<ItemErrorResponse> handleException(ItemNotFoundException ine){
        ItemErrorResponse errorResponse = new ItemErrorResponse();
        errorResponse.setStatus(HttpStatus.NOT_FOUND.value());
        errorResponse.setMessage(ine.getMessage());
        errorResponse.setTimeStamp(System.currentTimeMillis());
        return new ResponseEntity<ItemErrorResponse>(errorResponse,HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler
    public ResponseEntity<ItemErrorResponse> handleException(Exception ex){
        ItemErrorResponse errorResponse = new ItemErrorResponse();
        errorResponse.setStatus(HttpStatus.BAD_REQUEST.value());
        errorResponse.setMessage(ex.getMessage());
        errorResponse.setTimeStamp(System.currentTimeMillis());
        return new ResponseEntity<ItemErrorResponse>(errorResponse,HttpStatus.BAD_REQUEST);
    }
}

@ControllerAdvice is used to apply the exception handling technique across the application. This annotation can be used on top of the class; then the class will act as a controller-advice.

@ControllerAdvice is something similar to an interceptor or a filter. It Pre-processes the request to the controller and Post-process the response to handle exceptions.

Define exception handler methods using @ExceptionHandler annotation given by the Spring Framework. It is going to return ResponseEntity.

ResponseEntity is a simple wrapper of HTTP response object; it provides fine-grained control to specify HTTP status codes, HTTP headers and response body.

We are creating ItemRepository having all CRUD operations.

ItemRepository.java
package com.onlinetutorialspoint.repo;

import com.onlinetutorialspoint.exception.ItemNotFoundException;
import com.onlinetutorialspoint.model.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public class ItemRepository {

    @Autowired
    JdbcTemplate template;

    /*Getting all Items from table*/
    public List<Item> getAllItems(){
        List<Item> items = template.query("select id, name,category from item",(result,rowNum)->new Item(result.getInt("id"),
                result.getString("name"),result.getString("category")));
        return items;
    }
    /*Getting a specific item by item id from table*/
    public Item getItem(int itemId){
        Item item = null;
        String query = "SELECT * FROM ITEM WHERE ID=?";
        try{
            item = template.queryForObject(query,new Object[]{itemId},new BeanPropertyRowMapper<>(Item.class));
        }catch(Exception e){
            throw new ItemNotFoundException("Item Not Found : "+itemId);
        }
        return item;
    }
    /*Adding an item into database table*/
    public int addItem(Item item){
        String query = "INSERT INTO ITEM VALUES(?,?,?)";
        return template.update(query,new Object[] {
                Integer.valueOf(item.getId()),item.getName(),item.getCategory()
        });
    }
    /*delete an item from database*/
    public int deleteItem(int id){
        String query = "DELETE FROM ITEM WHERE ID =?";
        int size = template.update(query,id);
        if(size == 0){
            throw new ItemNotFoundException("No Item Found to delete: "+id);
        }
        return size;
    }

    /*update an item from database*/
    public void updateItem(Item item){
        String query = "UPDATE ITEM SET name=?, category=? WHERE id =?";
        template.update(query,
                new Object[] {
                        item.getName(),item.getCategory(), Integer.valueOf(item.getId())
                });
    }
}

Creating Spring Boot RestController class

ItemController.java
package com.onlinetutorialspoint.controller;

import com.onlinetutorialspoint.exception.ItemNotFoundException;
import com.onlinetutorialspoint.model.Item;
import com.onlinetutorialspoint.repo.ItemRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.List;

@RestController
public class ItemController {
    @Autowired
    ItemRepository itemRepo;

    @RequestMapping("/getAllItems")
    @ResponseBody
    public ResponseEntity<List<Item>> getAllItems(){
        List<Item> items =  itemRepo.getAllItems();
        return new ResponseEntity<List<Item>>(items, HttpStatus.OK);
    }

    @GetMapping("/item/{itemId}")
    @ResponseBody
    public ResponseEntity<Item> getItem(@PathVariable int itemId){
        if(itemId <= 0){
            throw new ItemNotFoundException("Invalid ItemId");
        }
        Item item = itemRepo.getItem(itemId);
        return new ResponseEntity<Item>(item, HttpStatus.OK);
    }

    @PostMapping(value = "/addItem",consumes = {"application/json"},produces = {"application/json"})
    @ResponseBody
    public ResponseEntity<Item> addItem(@RequestBody Item item,UriComponentsBuilder builder){
        itemRepo.addItem(item);
        HttpHeaders headers = new HttpHeaders();
        headers.setLocation(builder.path("/addItem/{id}").buildAndExpand(item.getId()).toUri());
        return new ResponseEntity<Item>(headers, HttpStatus.CREATED);
    }

    @PutMapping("/updateItem")
    @ResponseBody
    public ResponseEntity<Item> updateItem(@RequestBody Item item){
        if(item != null){
            itemRepo.updateItem(item);
        }
        return new ResponseEntity<Item>(item, HttpStatus.OK);
    }

    @DeleteMapping("/delete/{id}")
    @ResponseBody
    public ResponseEntity<Void> deleteItem(@PathVariable int id){
        itemRepo.deleteItem(id);
        return new ResponseEntity<Void>(HttpStatus.ACCEPTED);
    }
}

Main class

Application.java
package com.onlinetutorialspoint;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

Run the application:

console
mvn spring-boot:run
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Spring-Boot-ExceptionHandling 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.0.4.RELEASE:run (default-cli) > test-compile @ Spring-Boot-ExceptionHandling >>>
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ Spring-Boot-ExceptionHandling ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.7.0:compile (default-compile) @ Spring-Boot-ExceptionHandling ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ Spring-Boot-ExceptionHandling ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory E:\work\Spring-Boot-ExceptionHandling\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.7.0:testCompile (default-testCompile) @ Spring-Boot-ExceptionHandling ---
[INFO] No sources to compile
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.0.4.RELEASE:run (default-cli) < test-compile @ Spring-Boot-ExceptionHandling <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.0.4.RELEASE:run (default-cli) @ Spring-Boot-ExceptionHandling ---

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.4.RELEASE)

2018-08-27 11:40:24.880  INFO 4584 --- [           main] com.onlinetutorialspoint.Application     : Starting Application on DESKTOP-RN4SMHT with PID 4584 (E:\work\Spring-Boot-Excepti
onHandling\target\classes started by Lenovo in E:\work\Spring-Boot-ExceptionHandling)
2018-08-27 11:40:24.896  INFO 4584 --- [           main] com.onlinetutorialspoint.Application     : No active profile set, falling back to default profiles: default
2018-08-27 11:40:25.146  INFO 4584 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWeb
[email protected]: startup date [Mon Aug 27 11:40:25 IST 2018]; root of context hierarchy
2018-08-27 11:40:31.161  INFO 4584 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2018-08-27 11:40:31.317  INFO 4584 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2018-08-27 11:40:31.317  INFO 4584 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.32
2018-08-27 11:40:31.348  INFO 4584 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in pro
duction environments was not found on the java.library.path: [C:\Program Files\Java\jdk1.8.0_161\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\ProgramData\Oracle\Java
\javapath;C:\oraclexe\app\oracle\product\11.2.0\server\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\
MySQL\MySQL Server 5.5\bin;C:\php;C:\Apache24;C:\Apache24\bin;C:\Program Files\Java\jdk1.8.0_161\bin;D:\Softwares\apache-maven-3.5.2\bin;C:\Program Files\Git\cmd;C:\Program Files\Git
\mingw64\bin;C:\Program Files\Git\usr\bin;D:\Softwares\apache-ant-1.10.2\bin;C:\ProgramData\chocolatey\bin;;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\nodejs\;C:\Users\Lenovo\AppD
ata\Local\Programs\Python\Python36\Scripts\;C:\Users\Lenovo\AppData\Local\Programs\Python\Python36\;C:\Users\Lenovo\AppData\Local\Microsoft\WindowsApps;C:\Users\Lenovo\AppData\Local\
atom\bin;C:\Users\Lenovo\AppData\Local\Microsoft\WindowsApps;;C:\Program Files\Microsoft VS Code\bin;C:\Program Files\Docker Toolbox;C:\Users\Lenovo\AppData\Roaming\npm;.]
2018-08-27 11:40:31.567  INFO 4584 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2018-08-27 11:40:31.583  INFO 4584 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 6468 ms
2018-08-27 11:40:31.786  INFO 4584 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]
2018-08-27 11:40:31.786  INFO 4584 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-08-27 11:40:31.786  INFO 4584 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-08-27 11:40:31.786  INFO 4584 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-08-27 11:40:31.786  INFO 4584 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2018-08-27 11:40:32.911  INFO 4584 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.
web.servlet.resource.ResourceHttpRequestHandler]
2018-08-27 11:40:33.833  INFO 4584 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.Annota
[email protected]: startup date [Mon Aug 27 11:40:25 IST 2018]; root of context hierarchy
2018-08-27 11:40:34.041  INFO 4584 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/item/{itemId}],methods=[GET]}" onto public org.springframework.http.Res
ponseEntity<com.onlinetutorialspoint.model.Item> com.onlinetutorialspoint.controller.ItemController.getItem(int)
2018-08-27 11:40:34.056  INFO 4584 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/delete/{id}],methods=[DELETE]}" onto public org.springframework.http.Re
sponseEntity<java.lang.Void> com.onlinetutorialspoint.controller.ItemController.deleteItem(int)
2018-08-27 11:40:34.056  INFO 4584 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/getAllItems]}" onto public org.springframework.http.ResponseEntity<java
.util.List<com.onlinetutorialspoint.model.Item>> com.onlinetutorialspoint.controller.ItemController.getAllItems()
2018-08-27 11:40:34.056  INFO 4584 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/updateItem],methods=[PUT]}" onto public org.springframework.http.Respon
seEntity<com.onlinetutorialspoint.model.Item> com.onlinetutorialspoint.controller.ItemController.updateItem(com.onlinetutorialspoint.model.Item)
.........
.........

Access Application:

Getting all Items :

Springboot Exception Handling

Getting a specific Item:

Springboot Exception Handling 2

Getting unavailable Item:

Springboot Exception Handling

Deleting unavailable Item:

Springboot Exception Handling 11

Invalid Item code format (General Exception handler)

Springboot Exception Handling 12

Download Source from GIT:

Happy Learning 🙂