DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

SBOMs are essential to circumventing software supply chain attacks, and they provide visibility into various software components.

Related

  • Squid Game: The Clean Code Trials — A Java Developer's Survival Story
  • Jakarta EE 11 and the Road Ahead With Jakarta EE 12
  • How to Identify the Underlying Causes of Connection Timeout Errors for MongoDB With Java
  • How to Introduce a New API Quickly Using Micronaut

Trending

  • Master AI Development: The Ultimate Guide to LangChain, LangGraph, LangFlow, and LangSmith
  • Decoding the Secret Language of LLM Tokenizers
  • Evaluating Accuracy in RAG Applications: A Guide to Automated Evaluation
  • The Death of REST? Why gRPC and GraphQL Are Taking Over
  1. DZone
  2. Coding
  3. Frameworks
  4. Effective Exception Handling in Java and Spring Boot Applications

Effective Exception Handling in Java and Spring Boot Applications

Centralize your error handling using @ControllerAdvice and @ExceptionHandler to ensure consistent, maintainable exception management across your Spring Boot application.

By 
Arunkumar Kallyodan user avatar
Arunkumar Kallyodan
·
Jun. 17, 25 · Analysis
Likes (6)
Comment
Save
Tweet
Share
4.3K Views

Join the DZone community and get the full member experience.

Join For Free

When you're building Java applications, especially with frameworks like Spring Boot, it’s easy to overlook proper exception handling. However, poorly managed exceptions can make your application harder to debug, more difficult to maintain, and a nightmare for anyone dealing with production issues.

In this post, we’ll explore how to handle exceptions effectively by avoiding generic catch blocks, using custom exceptions, and centralizing exception handling with Spring Boot’s @ControllerAdvice.

Let’s dive in.

The Problem With Generic Catch Blocks

We've all seen this before:

Java
 
try {
    // risky code
} catch (Exception e) {
    e.printStackTrace(); // Not helpful in production!
}

It might seem like a simple catch-all approach, but it's actually a terrible practice. Here’s why:

  • It Swallows Real Issues: Catching Exception can hide bugs or other critical issues that need attention. If every exception is treated the same way, you might miss an important problem
  • Difficult to Debug: Simply printing the stack trace isn’t helpful in a production environment. You need to log the error with enough context to figure out what went wrong.

  • No Context: A generic catch(Exception e) doesn't tell you anything about the actual problem. You lose valuable information that could help you troubleshoot.

How We Fix It

Instead of catching Exception, always catch more specific exceptions. This gives you a clearer understanding of what went wrong and allows you to handle the error appropriately.

Java
 
try {
    // Risky operation
} catch (IOException e) {
    log.error("File reading error: " + e.getMessage());
    // Handle file reading error here
}


By catching IOException, you’re clearly indicating that the issue is related to input/output operations. It’s easier to identify and fix.

Creating and Using Custom Exceptions

Sometimes, the exceptions provided by Java or Spring aren't specific enough for your domain logic. This is where custom exceptions come in. When your application hits certain business rules or specific conditions, custom exceptions can provide clarity and better control over how to handle the problem.

  • Domain-Specific: When you create exceptions that reflect your application’s business logic, you make your code more readable.
  • Better Error Handling: Custom exceptions allow you to fine-tune how you handle specific errors in different parts of your application.

How to create custom exception? Let’s say you're building an application that handles users, and you need to handle cases where a user can’t be found.

Java
 
public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(String userId) {
        super("User not found: " + userId);
    }
}


When you try to fetch a user from the database and they don’t exist, you can throw this custom exception:

Java
 
User user = userRepository.findById(id)
    .orElseThrow(() -> new UserNotFoundException(id));


Centralized Exception Handling With @ControllerAdvice

Now that we have specific exceptions, how do we handle them consistently across our entire application? The answer is @ControllerAdvice. 

What is this annotation? Spring Boot’s @ControllerAdvice annotation allows you to define global exception handlers that can catch exceptions thrown by any controller and return a standardized response. This helps you avoid redundant exception handling code in every controller and keeps error responses consistent. 

For example: Create a global exception handler class using @ControllerAdvice:

Java
 
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<String> handleUserNotFound(UserNotFoundException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleAll(Exception ex) {
        return new ResponseEntity<>("An unexpected error occurred", HttpStatus.INTERNAL_SERVER_ERROR);
    }
}


  • The @ExceptionHandler annotation tells Spring Boot that the method should handle specific exceptions. In this case, if a UserNotFoundException is thrown anywhere in the app, the handleUserNotFound() method will handle it.
  • The handleAll() method catches any generic exception that’s not handled by other methods. This ensures that unexpected errors still get caught and return a meaningful response.

With @ControllerAdvice, we now have a centralized place to handle exceptions and return proper HTTP status codes and messages. This keeps our controller code clean and focused only on its primary responsibility—handling the business logic.

Use Structured Error Responses

When an error occurs, it's important to give the client (or anyone debugging) enough information to understand what went wrong. Returning just a generic error message isn't very helpful.

How to create structured error response? By creating a standard error response format, you can ensure consistency in error responses across your application.

Here’s how you might define an error response DTO:

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

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

    // Getters and Setters
}


Then in @ControllerAdvice

Java
 
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
    ErrorResponse error = new ErrorResponse(ex.getMessage(), HttpStatus.NOT_FOUND.value(), LocalDateTime.now());
    return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}


What is the advantage ?

Now, instead of returning just a plain string, you’re returning a structured response that includes:

  • Error message, HTTP status code and Timestamp
    This structure makes it much easier for the client (or even developers) to understand the error context.

Extra Tips for Better Exception Handling

  • Log with Context: Always log errors with relevant context—such as user ID, request ID, or any other relevant details. This makes troubleshooting easier.
  • Don’t Leak Sensitive Information: Avoid exposing sensitive information like stack traces or internal error details in production. You don’t want to expose your database schema or other internal workings to the outside world.
  • Carefully Choose HTTP Status Codes: Return the right HTTP status codes for different types of errors. For example, use 404 for "not found" errors, 400 for validation errors, and 500 for internal server errors.

Conclusion

Effective exception handling is crucial for building robust and maintainable applications. By avoiding generic catch blocks, using custom exceptions for clarity, and centralizing exception handling with @ControllerAdvice, you can greatly improve both the quality and the maintainability of your code. Plus, with structured error responses, you provide a better experience for the developers and consumers of your API.

So, the next time you find yourself writing a catch (Exception e), take a step back and consider whether there’s a more specific way to handle the error.

applications Java (programming language) Spring Boot

Opinions expressed by DZone contributors are their own.

Related

  • Squid Game: The Clean Code Trials — A Java Developer's Survival Story
  • Jakarta EE 11 and the Road Ahead With Jakarta EE 12
  • How to Identify the Underlying Causes of Connection Timeout Errors for MongoDB With Java
  • How to Introduce a New API Quickly Using Micronaut

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: