DEV Community

Pablo Mora
Pablo Mora

Posted on

Basic Exception Handling in Java Spring

Overview

In this post, we’re going to talk about what exceptions are and how to handle errors in a Java project using @ControllerAdvice. We’ll go over creating custom exceptions and sending back the right HTTP status codes. By the end, your API will be easier to maintain, more reliable, and give your users clearer error messages.

What is an exception?

Let’s start with the basics: what’s an exception?
An exception is basically Java’s way of saying, “Hey, something went wrong!” and it stops the normal happy flow of your program.
It usually occurs when something unexpected happens, like:

  • Trying to access a file that doesn’t exist.
  • Dividing by zero.
  • Our always friend, NullPointerException.

When an exception occurs:

  • Java creates an exception object with details about the error.
  • The regular flow of the application stops.
  • Java tries to find a matching handler to deal with the exception.

Types of exceptions

There are two main types of exceptions:

  • Checked exceptions → Must be either caught or declared (e.g., IOException).

  • Unchecked exceptions (Runtime exceptions) → Don’t require explicit handling, often indicate programming errors (e.g., NullPointerException).

In REST APIs, unchecked exceptions are the most common. Without proper handling, they return 500 Internal Server Error with a raw stack trace—which is not user-friend which is what we will try to improve later in this article.

Why Should we Handle Exceptions?

  • Prevent internal errors from leaking stack traces to clients.
  • Respond with meaningful HTTP status codes (404, 400, 500, etc.).
  • Provide consistent and clear error messages.
  • Centralize error handling.

Create custom exceptions

A good practice in any project is to define your own exception classes for common business errors, for example:

public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}
Enter fullscreen mode Exit fullscreen mode

Other examples of custom exceptions is BadRequestException, UnauthorizedException, etc

Use @ControllerAdvice for Global Exception Handling

Use @ControllerAdvice to define that this class will be the exception handler for the project.

@ControllerAdvice
public class GlobalExceptionHandler {

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

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGeneral(Exception ex) {
        // this will catch any exception extending from Excepcion.class
        return new ResponseEntity<>("Internal server error", HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
Enter fullscreen mode Exit fullscreen mode

Throw exceptions in your services or controllers

Now that we've defined our custom exceptions, and the global exception handler then we can safely throw exceptions.

@GetMapping("/items/{id}")
public Item getItem(@PathVariable Long id) {
    return itemRepository.findById(id)
            .orElseThrow(() -> new ResourceNotFoundException("Item with ID " + id + " not found"));
}
Enter fullscreen mode Exit fullscreen mode

Client Response Examples

With everything defined we will have nice and well understanding exceptions like the following.

Resource Not Found

When requesting a non-existent resource:

HTTP/1.1 404 Not Found
Body: "Item with ID 10 not found"
Enter fullscreen mode Exit fullscreen mode

For internal errors (e.g., unhandled NullPointerException):

HTTP/1.1 500 Internal Server Error
Body: "Internal server error"
Enter fullscreen mode Exit fullscreen mode

Conclusion

Handling exceptions centrally with @ControllerAdvice and using custom exceptions helps you build clean, robust APIs. This improves the client experience and makes your code easier to maintain.

Top comments (0)