Global exception handling in spring boot
Imagine you have a Spring Boot application with multiple controllers and service methods. During the execution of these components, various exceptions can occur, such as database errors, validation failures, or unexpected runtime errors. Handling these exceptions separately in each component can lead to code duplication and inconsistency.
Global exception handling addresses this issue by allowing you to define a centralized exception handler that intercepts and manages all exceptions thrown by your application. This handler can perform tasks such as logging the error details, returning custom error responses to clients, or taking appropriate recovery actions.
Global exception handling in Spring Boot is a powerful feature that allows you to centralize the handling of exceptions thrown by your application. This can help you maintain clean and consistent error responses, log errors, and perform other necessary actions when exceptions occur.
Spring Boot provides several annotations and classes to achieve global exception handling. The most common approach is using the
@ControllerAdviceannotation combined with@ExceptionHandlermethods.
@ControllerAdvice and @ExceptionHandler
The @ControllerAdvice annotation allows you to define a class that will handle exceptions globally across all controllers. Within this class, you can use the @ExceptionHandler annotation to define methods that handle specific types of exceptions.
@ResponseStatus
You can also use the @ResponseStatus annotation to define the HTTP status code for a specific exception. This can be useful for custom exceptions that you define in your application.
Let’s create a complete example of a Spring Boot application with global exception handling, including a Student object. We'll define a REST controller for managing students, handle exceptions globally, and provide custom error responses.
Step 1: Create a Student Class
public class Student {
private Long id;
private String name;
private Integer age;
// Constructors, Getters, and Setters
public Student(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}Step 2: Create a StudentService Class
This service class will have methods to retrieve a student by ID. For demonstration purposes, it will throw a custom exception if the student is not found.
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class StudentService {
private static Map<Long, Student> students = new HashMap<>();
static {
students.put(1L, new Student(1L, "John Doe", 20));
students.put(2L, new Student(2L, "Jane Doe", 22));
}
public Student getStudentById(Long id) {
if (!students.containsKey(id)) {
throw new StudentNotFoundException("Student with ID " + id + " not found");
}
return students.get(id);
}
}Step 3: Create a Custom Exception
public class StudentNotFoundException extends RuntimeException {
public StudentNotFoundException(String message) {
super(message);
}
}Step 4: Create a Global Exception Handler
The global exception handler is responsible for catching exceptions thrown by any controller or service in the application and returning an appropriate response to the client.
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(StudentNotFoundException.class)
public ResponseEntity<?> handleStudentNotFoundException(StudentNotFoundException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(HttpStatus.NOT_FOUND.value(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleGlobalException(Exception ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
}Step 5: Create an ErrorDetails Class
This class will be used to structure the error response.
public class ErrorDetails {
private int statusCode;
private String message;
private String details;
public ErrorDetails(int statusCode, String message, String details) {
this.statusCode = statusCode;
this.message = message;
this.details = details;
}
// Getters and Setters
public int getStatusCode() {
return statusCode;
}
public void setStatusCode(int statusCode) {
this.statusCode = statusCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
}Step 6: Create a StudentController Class
This controller will expose a REST endpoint to get a student by ID.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/students/{id}")
public Student getStudentById(@PathVariable Long id) {
return studentService.getStudentById(id);
}
}Step 7: Testing the Global Exception Handling
When you run the application and try to retrieve a student by an ID that does not exist, the global exception handler will catch the StudentNotFoundException and return a 404 Not Found response with the error details.
Example Output
Request:
GET /students/3
Response:
{
"statusCode": 404,
"message": "Student with ID 3 not found",
"details": "uri=/students/3"
}If any other unexpected exception occurs, the global exception handler will catch it and return a 500 Internal Server Error response.
Summary
- @ControllerAdvice: A specialization of the
@Componentannotation that allows defining global exception handling for controllers. - @ExceptionHandler: Used to define the method that will handle specific exceptions.
This setup allows you to handle exceptions consistently across your Spring Boot application, providing a better experience for users and easier maintenance for developers.
Using @ResponseStatus annotation
You can use the @ResponseStatus annotation to directly set the HTTP status code and message for a specific exception. This approach is simpler and avoids the need for a global exception handler class, though it is less flexible for complex scenarios.
We will use the @ResponseStatus annotation to set the HTTP status code and reason for the StudentNotFoundException.
Create the Student Class
The Student class remains the same as before.
public class Student {
private Long id;
private String name;
private Integer age;
// Constructors, Getters, and Setters
public Student(Long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}Modify the Custom Exception with @ResponseStatus
We will use the @ResponseStatus annotation to set the HTTP status code and reason for the StudentNotFoundException.
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Student not found")
public class StudentNotFoundException extends RuntimeException {
public StudentNotFoundException(String message) {
super(message);
}
}value: The HTTP status code to return (e.g.,HttpStatus.NOT_FOUND).reason: The reason phrase that will be returned to the client.
The StudentService class remains unchanged. It will throw the StudentNotFoundException if the student is not found.
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class StudentService {
private static Map<Long, Student> students = new HashMap<>();
static {
students.put(1L, new Student(1L, "John Doe", 20));
students.put(2L, new Student(2L, "Jane Doe", 22));
}
public Student getStudentById(Long id) {
if (!students.containsKey(id)) {
throw new StudentNotFoundException("Student with ID " + id + " not found");
}
return students.get(id);
}
}Update the StudentController Class
The StudentController class remains unchanged.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StudentController {
@Autowired
private StudentService studentService;
@GetMapping("/students/{id}")
public Student getStudentById(@PathVariable Long id) {
return studentService.getStudentById(id);
}
}Testing the Application
When you run the application and try to retrieve a student by an ID that does not exist, the @ResponseStatus annotation will automatically set the HTTP status code and reason phrase.
Example Output
Request:
GET /students/3
Response:
{
"timestamp": "2024-09-01T12:00:00.000+00:00",
"status": 404,
"error": "Not Found",
"message": "Student not found",
"path": "/students/3"
}Summary
- @ResponseStatus: Used to mark a method or an exception class with the status code and reason that should be returned.
- This approach is simple and works well for straightforward use cases where you don’t need complex error handling logic.
This method offers a quick and clear way to define the response status and message for specific exceptions in your Spring Boot application.
👏 If you found my articles useful, please consider giving it claps and sharing it with your friends and colleagues.





