#
@ControllerAdvice & @ExceptionHandler example
This tutorial give you an example of @ControllerAdvice & @ExceptionHandler annotations usage in Spring.
#
Example
This example started from Spring initializr.
Once the application downloaded from Spring initializr is running, we will add the following classes:
Employee.java
package com.example.exceptions.demo.model;
public class Employee {
private Integer id;
private String name;
private String department;
private int age;
public Employee(Integer id, String name, String department, int age) {
this.id = id;
this.name = name;
this.department = department;
this.age = age;
}
public int 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 getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
EmployeeController.java
package com.example.exceptions.demo.controller;
import com.example.exceptions.demo.model.Employee;
import com.example.exceptions.demo.service.EmployeeService;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
@RestController
public class EmployeeController {
@Autowired
EmployeeService employeeService;
@GetMapping("/employee/all")
String getAllEmployees() {
ArrayList<Employee> allEmployees = employeeService.getEmployees();
String json = new Gson().toJson(allEmployees);
return json;
}
@PutMapping(value="/employee/add", consumes = "application/json")
int addEmployee(@RequestBody Employee newEmployee) {
employeeService.addEmployee(newEmployee);
int newCount = employeeService.countEmployees();
return newCount;
}
@DeleteMapping(value="/employee/delete", consumes = "application/json")
int deleteEmployee(@RequestBody String empId) {
employeeService.removeEmployee(empId);
int newCount = employeeService.countEmployees();
return newCount;
}
@DeleteMapping(value="/employee/deleteAll")
int deleteEmployee() {
employeeService.removeEmployeeAll();
int newCount = employeeService.countEmployees();
return newCount;
}
}
LowIdException.java
package com.example.exceptions.demo.exception.functional;
public class LowIdException extends RuntimeException {
private static final long serialVersionUID = 1L;
public LowIdException(String message) {
super(message);
}
public LowIdException(String message, Throwable cause) {
super(message, cause);
}
public LowIdException(Integer id, String message) {
super(message);
System.out.println("Id too low.");
}
}
EmployeeService.java
package com.example.exceptions.demo.service;
import com.example.exceptions.demo.exception.functional.LowIdException;
import com.example.exceptions.demo.model.Employee;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
@Service
public class EmployeeService {
private HashMap<Integer, Employee> employees = new HashMap<>();
public void addEmployee(Employee emp) {
System.out.println("emp.getId()="+emp.getId());
if (emp.getId() < 100) {
System.out.println("Id lower than 100");
throw new LowIdException(emp.getId(), "Message #1");
}
this.employees.put(emp.getId(), emp);
}
public void removeEmployee(String empId) {
this.employees.remove(empId);
}
public void removeEmployeeAll() {
this.employees.clear();
}
public int countEmployees() {
return this.employees.size();
}
public ArrayList<Employee> getEmployees() {
ArrayList<Employee> empList = new ArrayList<>();
// go through the values of the map
for (Employee emp : employees.values()) {
empList.add(emp);
}
return empList;
}
}
ErrorResponse.java
package com.example.exceptions.demo.model;
public class ErrorResponse {
private String errorCode;
private String errorMessage;
public ErrorResponse(String errorCode, String errorMessage) {
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
// Getters and setters
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
GlobalExceptionHandler.java
package com.example.exceptions.demo.exception;
import com.example.exceptions.demo.exception.functional.LowIdException;
import com.example.exceptions.demo.model.ErrorResponse;
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;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(LowIdException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ErrorResponse handleLowIdException(LowIdException ex) {
System.out.println("in handleLowIdException method");
return new ErrorResponse("LowIdException", ex.getMessage());
}
}
With the code running we send the following request:
In the Console logs we will see the following:
emp.getId()=10
Id lower than 100
Id too low.
in handleLowIdException method
and the request response will be:
{
"errorCode": "LowIdException",
"errorMessage": "Message #1"
}
Now we comment the handleLowIdException method:
GlobalExceptionHandler.java
package com.example.exceptions.demo.exception;
import com.example.exceptions.demo.exception.functional.LowIdException;
import com.example.exceptions.demo.model.ErrorResponse;
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;
@ControllerAdvice
public class GlobalExceptionHandler {
// @ExceptionHandler(LowIdException.class)
// @ResponseStatus(HttpStatus.BAD_REQUEST)
// @ResponseBody
// public ErrorResponse handleLowIdException(LowIdException ex) {
// System.out.println("in handleLowIdException method");
// return new ErrorResponse("LowIdException", ex.getMessage());
// }
}
We run the application and we send the same request again.
In the Console logs we will see something like this:
emp.getId()=10
Id lower than 100
Id too low.
2024-09-02T14:33:32.577+03:00 ERROR 5456 --- [exceptions-demo] [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: com.example.exceptions.demo.exception.functional.LowIdException: Message #1] with root cause
com.example.exceptions.demo.exception.functional.LowIdException: Message #1
at com.example.exceptions.demo.service.EmployeeService.addEmployee(EmployeeService.java:19) ~[classes/:na]
at com.example.exceptions.demo.controller.EmployeeController.addEmployee(EmployeeController.java:27) ~[classes/:na]
and the request response will be:
{
"timestamp": "2024-09-02T11:33:32.590+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/employee/add"
}
#
Conclusions
Info
- @ControllerAdvice can be viewed as an interceptor of exceptions thrown by methods annotated with @RequestMapping and similar;
- The exception handler is marked using @ExceptionHandler annotation.
- Without an ExceptionHandler, the response has a bad format and the log information is longer.