# @ControllerAdvice & @ExceptionHandler example

This tutorial give you an example of @ControllerAdvice & @ExceptionHandler annotations usage in Spring.

# Example

This example started from Spring initializr.

1-spring-initializr.png
1-spring-initializr.png

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:

2-spring-exception1.png
2-spring-exception1.png

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