Introduction

This post will show you an example on exception handling in Spring web application. In other words, we can also say exception handling in Spring MVC. We will use handle exception on Spring REST APIs but not limited to Spring REST APIs. You can create Spring MVC application and handle the exception same way. We will use @ControllerAdvice and @ExceptionHandler annotations to handle exception in Spring application.

Related Posts:

Prerequisites

Eclipse, Java 1.8, Gradle 4.10.2, Spring Boot 2.1.5

Example with Source Code

Creating Project

Create a gradle based project in Eclipse, the project name is spring-web-exception-handling.

Updating Build Script

Update the default generated build script with the following code.

We will use in-memory database h2 to do quick PoC. Using in-memory database you don’t need to define datasource, you don’t need to define table creation SQL script. The entity class will create the required table in the h2 database.

We are using Spring Data JPA to get benefits of built-in query out of the box using Java methods.

buildscript {
	ext {
		springBootVersion = '2.1.5.RELEASE'
	}
    repositories {
    	mavenLocal()
    	mavenCentral()
    }
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
	mavenLocal()
    mavenCentral()
}
dependencies {
	compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
	compile("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}")
	runtime("com.h2database:h2:1.4.196")
}

Creating Entity Class

We need to create entity class to perform database operations. This class will map the Java fields to table columns.

In the below entity class we are using auto-generated primary key. The table column name will be same as the Java class attribute unless you mention the column name using @Column annotation.

package com.roytuts.spring.web.exception.handling.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Employee {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	private String name;
	@Column(name = "email_address")
	private String email;
	public Employee() {
	}
	public Employee(Integer id, String name, String email) {
		this.id = id;
		this.name = name;
		this.email = email;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
}

Creating Repository Interface

We are using Spring Data JPA repository, so we need to create an interface for query the database.

As we know Spring Data JPA provides query out of the box by using buit-in methods or using custom methods, so we are creating below two methods to find out employee by name or email address.

package com.roytuts.spring.web.exception.handling.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.roytuts.spring.web.exception.handling.entity.Employee;
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
	Employee findByName(String name);
	Employee findByEmail(String email);
}

Creating Exception Class

We will create custom exception class to throw exception when an employee is not found in the database for a given id, name or email address.

By creating the custom exception class we are also showing the nice message to the end users instead of default 404 message.

package com.roytuts.spring.web.exception.handling.exception;
public class EmployeeNotFoundException extends RuntimeException {
	private static final long serialVersionUID = 1L;
	public EmployeeNotFoundException(Integer id) {
		super("Could not find employee with id " + id);
	}
	public EmployeeNotFoundException(String str) {
		super("Could not find employee with name or email " + str);
	}
}

Creating Advice

When an EmployeeNotFoundException is thrown, this extra Spring MVC configuration is used to render an HTTP 404:

package com.roytuts.spring.web.exception.handling.advice;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import com.roytuts.spring.web.exception.handling.exception.EmployeeNotFoundException;
@ControllerAdvice
public class EmployeeNotFoundAdvice {
	@ResponseBody
	@ExceptionHandler(EmployeeNotFoundException.class)
	@ResponseStatus(HttpStatus.NOT_FOUND)
	String employeeNotFoundHandler(EmployeeNotFoundException ex) {
		return ex.getMessage();
	}
}

@ResponseBody signals that this advice is rendered straight into the response body.

@ExceptionHandler configures the advice to only respond if an EmployeeNotFoundException is thrown.

@ResponseStatus says to issue an HttpStatus.NOT_FOUND, i.e. an HTTP 404.

The body of the advice generates the content. In this case, it gives the message of the exception.

Creating Spring REST Controller

Create below Spring REST controller to wrap your repository into web layer.

We are using here only @GetMapping, if you want other http request mapping, you can use.

@RestController indicates that the data returned by each method will be written straight into the response body instead of rendering a template.

An EmployeeRepository is injected by constructor into the controller.

EmployeeNotFoundException is an exception used to indicate when an employee is looked up but not found.

package com.roytuts.spring.web.exception.handling.rest.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.roytuts.spring.web.exception.handling.entity.Employee;
import com.roytuts.spring.web.exception.handling.exception.EmployeeNotFoundException;
import com.roytuts.spring.web.exception.handling.repository.EmployeeRepository;
@RestController
public class EmployeeRestController {
	@Autowired
	private EmployeeRepository employeeRepository;
	@GetMapping("/employees")
	public ResponseEntity<List<Employee>> getEmployees() {
		return new ResponseEntity<List<Employee>>(employeeRepository.findAll(), HttpStatus.OK);
	}
	@GetMapping("/employee/id/{id}")
	public ResponseEntity<Employee> getEmployeeById(@PathVariable Integer id) {
		Employee employee = employeeRepository.findById(id).orElseThrow(() -> new EmployeeNotFoundException(id));
		return new ResponseEntity<Employee>(employee, HttpStatus.OK);
	}
	@GetMapping("/employee/name/{name}")
	public ResponseEntity<Employee> getEmployeeByName(@PathVariable String name) {
		Employee employee = employeeRepository.findByName(name);
		if (employee == null) {
			throw new EmployeeNotFoundException(name);
		}
		return new ResponseEntity<Employee>(employee, HttpStatus.OK);
	}
	@GetMapping("/employee/email/{email}")
	public ResponseEntity<Employee> getEmployeeByEmail(@PathVariable String email) {
		Employee employee = employeeRepository.findByEmail(email);
		if (employee == null) {
			throw new EmployeeNotFoundException(email);
		}
		return new ResponseEntity<Employee>(employee, HttpStatus.OK);
	}
}

Creating Main Class

In Spring Boot application, a public static void main entry point and @SpringBootApplication are enough to launch the application.

package com.roytuts.spring.web.exception.handling;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EntityScan("com.roytuts.spring.web.exception.handling.entity")
@EnableJpaRepositories("com.roytuts.spring.web.exception.handling.repository")
@SpringBootApplication(scanBasePackages = "com.roytuts.spring.web.exception.handling")
public class SpringWebExceptionHandlingApp {
	public static void main(String[] args) {
		SpringApplication.run(SpringWebExceptionHandlingApp.class, args);
	}
}

Testing the Application

Run the above main class to deploy the application into embedded Tomcat server.

Hit the URL http://localhost:8080/employees, you will see below output:

exception handling in spring mvc

Hit the URL http://localhost:8080/employee/id/1, you will see below output:

exception in spring mvc

Hit the URL http://localhost:8080/employee/id/8, you will see below output:

exception in spring web

Similarly you can test for a given email or name.

Source Code

You can download source code.

Thanks for reading.

Tags:

Leave a Reply

Your email address will not be published. Required fields are marked *