Spring Boot @ExceptionHandler
last modified July 16, 2023
Spring Boot @ExceptionHandler tutorial shows how to handle exceptions with Spring @ExceptionHandler.
Spring is a popular Java application framework and Spring Boot is an evolution of Spring that helps create stand-alone, production-grade Spring based applications easily.
@ExceptionHandler
is an annotation for handling exceptions in
specific handler classes or handler methods. In Servlet environments, we can
combine the @ExceptionHandler
annotation with
@ResponseStatus
to define the response status for the HTTP
response.
Spring Boot @ExceptionHandler example
In the following application, we demonstrate the usage of the
@ExceptionHandler
. A HTML link in the home page calls a
controller's method, which either returns data or throws an exception.
build.gradle ... src ├── main │ ├── java │ │ └── com │ │ └── zetcode │ │ ├── Application.java │ │ ├── controller │ │ │ └── MyController.java │ │ ├── exception │ │ │ └── MyDataException.java │ │ └── service │ │ ├── IDataService.java │ │ └── MyDataService.java │ └── resources │ ├── static │ │ ├── index.html │ │ └── showError.html │ └── templates │ └── showData.ftlh └── test ├── java └── resources
This is the project structure of the Spring application.
plugins { id 'org.springframework.boot' version '3.1.1' id 'io.spring.dependency-management' version '1.1.0' id 'java' } group = 'com.zetcode' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-freemarker' }
This is the Gradle build file. The spring-boot-starter-freemarker
is a dependency for Freemarker template engine.
package com.zetcode.controller; import com.zetcode.exception.MyDataException; import com.zetcode.service.IDataService; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import java.util.HashMap; import java.util.Map; @Controller public class MyController { private final IDataService dataService; public MyController(IDataService dataService) { this.dataService = dataService; } @RequestMapping(value = "/getData") public ModelAndView getData() { var data = dataService.findAll(); Map<String, Object> params = new HashMap<>(); params.put("values", data); return new ModelAndView("showData", params); } @ExceptionHandler(MyDataException.class) public String handleError(MyDataException e) { return "redirect:/showError.html"; } }
The MyController's
getData
method calls a service
method and stores the retrieved data into a list. The data is sent to the
showData
view. In case of a MyDataException
, the
controller redirects to an error page.
@ExceptionHandler(MyDataException.class) public String handleError(MyDataException e) { return "redirect:/showError.html"; }
The handleError
is decorated with @ExceptionHandler
.
The handler is activated for the MyDataException
. In the body of
the method, we redirect to the showError.html
page.
package com.zetcode.exception; public class MyDataException extends RuntimeException { public MyDataException(String message) { super(message); } }
We define a custom MyDataException
.
package com.zetcode.service; import java.util.List; public interface IDataService { List<String> findAll(); }
IDataService
contains the contract method.
package com.zetcode.service; import com.zetcode.exception.MyDataException; import java.util.ArrayList; import java.util.List; import java.util.Random; import org.springframework.stereotype.Service; @Service public class MyDataService implements IDataService { @Override public List<String> findAll() { var r = new Random(); if (r.nextBoolean()) { throw new MyDataException("Failed to retrieve data"); } var data = new ArrayList<String>(); data.add("yellow moon"); data.add("brisk pace"); data.add("empty bottle"); data.add("beautiful weather"); return data; } }
MyDataService
implements IDataService's
findAll
method. The method either returns data or throws a
MyDataException
.
var r = new Random(); if (r.nextBoolean()) { throw new MyDataException("Failed to retrieve data"); }
The findAll
method randomly throws MyDataException
.
The exception is then handled in the controller.
var data = new ArrayList<>(); data.add("yellow moon"); data.add("brisk pace"); data.add("empty bottle"); data.add("beautiful weather"); return data;
When there is no exception, we return a list of strings.
<!DOCTYPE html> <html> <head> <title>Home page</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <a href="/getData">Get data</a> </body> </html>
This is the home page. It contains a link that calls our controller method to fetch some data.
<!DOCTYPE html> <html> <head> <title>Error</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <p>Failed to retrieve data</p> </body> </html>
This is an error page. It is shown when MyDataException
is thrown.
<!DOCTYPE html> <html> <head> <title>Data</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <h2>Data</h2> <ul> <#list values as val> <li><td>${val}</td></li> </#list> </ul> </body> </html>
The showData.ftlh
is a Freemarker template file which shows all
retrieved data in an HTML list.
package com.zetcode; 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); } }
Application
is the entry point which bootstraps Spring Boot
application.
In this article we have shown how to handle exceptions in a Spring application
with @ExceptionHandler
.