Exception Handling in Spring Boot Application [2025 Edition]

Exception handling is a crucial part of building robust APIs in Spring Boot. In real-world applications, unexpected errors can occur due to invalid input, missing resources, or internal server issues. Proper exception handling helps in:

✅ Returning meaningful error messages to the client.
Improving API reliability and preventing crashes.
✅ Enhancing debugging and logging.

🔹 Reference to the Previous Tutorial

In our previous tutorial, we built a RESTful API for User Management using Spring Boot, MySQL, and Spring Data JPA. Now, we will extend that tutorial by implementing exception handling in the same application.

Prerequite tutorial: Building RESTful Web Services Using Spring Boot, Spring Data JPA, and MySQL


🚀 Step 1: Create a Custom Error Response Class

📌 We need a standard structure to return error messages when exceptions occur.

📌 Create ErrorResponse.java inside net.javaguides.usermanagement.exception

package net.javaguides.usermanagement.exception;

import java.time.LocalDateTime;

public class ErrorResponse {
    private String message;
    private int status;
    private LocalDateTime timestamp;

    public ErrorResponse(String message, int status) {
        this.message = message;
        this.status = status;
        this.timestamp = LocalDateTime.now();
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public LocalDateTime getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(LocalDateTime timestamp) {
        this.timestamp = timestamp;
    }
}

📌 Explanation

✔️ message → Stores the error message.
✔️ status → Stores the HTTP status code (e.g., 404, 500).
✔️ timestamp → Stores the time the error occurred.
✔️ Constructor initializes timestamp automatically.

🚀 Step 2: Create a Global Exception Handler

Instead of handling exceptions inside each controller, we will use @RestControllerAdvice to centralize exception handling.

📌 Create GlobalExceptionHandler.java inside net.javaguides.usermanagement.exception

package net.javaguides.usermanagement.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    // Handle ResourceNotFoundException
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
        ErrorResponse errorResponse = new ErrorResponse(
            ex.getMessage(),
            HttpStatus.NOT_FOUND.value()
        );
        return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
    }

    // Handle Generic Exceptions
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
        ErrorResponse errorResponse = new ErrorResponse(
            "An unexpected error occurred: " + ex.getMessage(),
            HttpStatus.INTERNAL_SERVER_ERROR.value()
        );
        return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

📌 Explanation of GlobalExceptionHandler.java

✔️ @RestControllerAdvice → Makes this class a global exception handler for all controllers.
✔️ @ExceptionHandler(ResourceNotFoundException.class) → Handles cases where a user is not found in the database.
✔️ @ExceptionHandler(Exception.class) → Catches all other unhandled exceptions and returns a 500 (Internal Server Error).

🚀 Step 3: Create ResourceNotFoundException

📌 Create ResourceNotFoundException.java inside net.javaguides.usermanagement.exception

package net.javaguides.usermanagement.exception;

public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

🚀 Step 4: Modify the Service Layer to Use ResourceNotFoundException

📌 Modify UserServiceImpl.java inside net.javaguides.usermanagement.service.impl

@Override
public UserDto getUserById(Long id) {
    User user = userRepository.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id));
    return userMapper.toDto(user);
}

@Override
public UserDto updateUser(Long id, UserDto userDto) {
    User user = userRepository.findById(id)
        .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + id));

    user.setFirstName(userDto.firstName());
    user.setLastName(userDto.lastName());
    user.setEmail(userDto.email());
    user.setDateOfBirth(userDto.dateOfBirth());

    User updatedUser = userRepository.save(user);
    return userMapper.toDto(updatedUser);
}

@Override
public void deleteUser(Long id) {
    if (!userRepository.existsById(id)) {
        throw new ResourceNotFoundException("User not found with id: " + id);
    }
    userRepository.deleteById(id);
}

📌 Explanation

✔️ Uses orElseThrow() to throw ResourceNotFoundException if a user does not exist.
✔️ Helps return 404 Not Found instead of null values.

🚀 Step 5: Testing Exception Handling Using Postman

✅ 1. Testing ResourceNotFoundException

❌ Get a Non-Existent User (GET Request)

📌 GET Request URL:

http://localhost:8080/api/users/100
Exception Handling in Spring Boot Application [2025 Edition]

✅ 2. Testing Generic Exception Handling

❌ Invalid URL (POST Request)

📌 POST Request URL:

Valid URL: http://localhost:8080/api/users
Invalid URL: http://localhost:8080/api/users/1
Exception Handling in Spring Boot Application [2025 Edition]

🚀 Step 6: Logging Exceptions (Optional, Recommended)

To improve debugging, we can log errors when exceptions occur.

📌 Modify GlobalExceptionHandler.java to include logging

package net.javaguides.usermanagement.exception;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleResourceNotFoundException(ResourceNotFoundException ex) {
        logger.error("Resource Not Found: " + ex.getMessage());
        ErrorResponse errorResponse = new ErrorResponse(ex.getMessage(), HttpStatus.NOT_FOUND.value());
        return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
        logger.error("Internal Server Error: " + ex.getMessage(), ex);
        ErrorResponse errorResponse = new ErrorResponse("An unexpected error occurred: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.value());
        return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

📌 Explanation

✔️ Uses SLF4J Logger to log errors for better debugging.
✔️ Helps track errors in production environments.

🎯 Summary: What We Achieved

✔️ Implemented exception handling in Spring Boot.
✔️ Created a GlobalExceptionHandler for centralized error handling.
✔️ Handled ResourceNotFoundException for missing users.
✔️ Tested exception handling using Postman.
✔️ Added logging for better debugging.

🚀 Next Steps

🔹 Add Spring Security for authentication & authorization.
🔹 Implement custom validation for user input.
🔹 Deploy the application to AWS, Heroku, or Docker.

🎉 Congratulations! You now have a fully functional, production-ready Spring Boot API with proper exception handling! 🚀🔥

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare