Exception Handling in Spring Boot – Explained

Exception handling in Spring Boot is not merely about catching runtime errors; it is about controlling the failure lifecycle within the request-processing pipeline. From the moment an exception is thrown, Spring’s DispatcherServlet coordinates with exception resolvers, controller advice components, and response converters to transform failures into structured, semantically meaningful HTTP responses that align with REST principles.

Exception Handling in Spring Boot

In production systems, exceptions happen frequently due to:

  • Invalid client input
  • Database failures
  • Network timeouts
  • Business rule violations
  • Third-party API failures

A mature application does not crash or leak stack traces. Instead, it responds with structured, meaningful information.

This article explains both how to implement it and how it works internally inside Spring Boot.


What Happens When an Exception Occurs in Spring Boot?

Let’s first understand the internal flow.

Request Lifecycle (Simplified)

Client Request
     ↓
DispatcherServlet
     ↓
Controller
     ↓
Service
     ↓
Repository

If an exception occurs anywhere:

  • Controller
  • Service
  • Repository
  • Filter/Interceptor

It travels back up the chain.

Without custom handling, Spring Boot’s default behavior is:

  • Return HTTP 500
  • Send a generic error JSON
  • Log stack trace in console

Internal Mechanism Behind Exception Handling

Spring Boot uses several internal components:

DispatcherServlet (Core Controller)

This is the front controller of Spring MVC.

When an exception occurs, DispatcherServlet:

  1. Stops normal execution
  2. Searches for an ExceptionResolver
  3. Delegates exception handling

HandlerExceptionResolver (Key Player)

Spring maintains a list of resolvers:

  1. ExceptionHandlerExceptionResolver
    • Handles @ExceptionHandler methods
    • Works with @ControllerAdvice
  2. ResponseStatusExceptionResolver
    • Handles exceptions annotated with @ResponseStatus
  3. DefaultHandlerExceptionResolver
    • Handles Spring internal exceptions
    • Example: HttpRequestMethodNotSupported

Spring checks these resolvers in order.

The first one that can handle the exception wins.


Why Production Systems Need Global Handling

Handling exceptions inside each controller:

  • Duplicates code
  • Creates inconsistency
  • Hard to maintain
  • Not scalable

Global handling centralizes logic.


Production-Ready Strategy

A professional approach includes:

  • Custom exceptions
  • Global handler
  • Structured error response
  • Proper logging
  • Validation handling
  • Standardized formats

Structured Error Response (Core Concept)

Returning plain strings is bad practice.

Instead, define a standard response.

public class ApiError {

    private int status;
    private String error;
    private String message;
    private String path;
    private LocalDateTime timestamp;

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

Why this matters

Clients can reliably parse:

  • status
  • message
  • time
  • endpoint

This is critical for frontend and microservices.


Custom Exceptions (Business Clarity)

Custom exceptions describe intent.

public class ResourceNotFoundException
        extends RuntimeException {

    public ResourceNotFoundException(String msg) {
        super(msg);
    }
}

Usage:

User user = repo.findById(id)
        .orElseThrow(() ->
            new ResourceNotFoundException("User not found"));

Now your code reads like business logic, not error handling.


@ControllerAdvice (Global Control Center)

This is where production-level handling lives.

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ApiError> handleNotFound(
            ResourceNotFoundException ex,
            HttpServletRequest request) {

        ApiError error = new ApiError(
                404,
                "NOT_FOUND",
                ex.getMessage(),
                request.getRequestURI()
        );

        return new ResponseEntity<>(error,
                HttpStatus.NOT_FOUND);
    }
}

Internal Working of @ControllerAdvice

When an exception occurs:

  1. DispatcherServlet catches it
  2. It asks HandlerExceptionResolvers
  3. ExceptionHandlerExceptionResolver checks for:
    • @ExceptionHandler methods
    • inside @ControllerAdvice
  4. Matching handler executes
  5. ResponseEntity is returned to client

So @ControllerAdvice acts like a global interceptor for exceptions.


Validation Exception Handling

When using:

@PostMapping
public void create(@Valid @RequestBody UserDto dto)

Spring automatically validates.

If invalid:

  • MethodArgumentNotValidException is thrown
  • Occurs BEFORE controller logic executes

Handler:

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>>
handleValidation(MethodArgumentNotValidException ex) {

    Map<String, String> errors = new HashMap<>();

    ex.getBindingResult()
      .getFieldErrors()
      .forEach(err ->
         errors.put(err.getField(),
                    err.getDefaultMessage()));

    return ResponseEntity.badRequest()
                         .body(errors);
}

Logging Strategy (Critical in Production)

Never return stack traces to clients.

But ALWAYS log them.

log.error("Unexpected error occurred", ex);

Why?

  • Debug production incidents
  • Root cause analysis
  • Monitoring tools (ELK, Datadog)

Spring Boot 3 — ProblemDetail (RFC-7807 Support)

In modern APIs, returning ad-hoc error JSON leads to inconsistency and tight coupling between backend and clients. To solve this, Spring Boot 3 introduced native support for RFC-7807 (Problem Details for HTTP APIs) through the ProblemDetail class.

This is a major improvement in Exception Handling in Spring Boot – Explained, because it moves error handling from custom conventions to an industry-standard specification.

Basic Example

@ExceptionHandler(ResourceNotFoundException.class)
public ProblemDetail handleNotFound(
        ResourceNotFoundException ex) {

    ProblemDetail pd =
        ProblemDetail.forStatus(404);

    pd.setTitle("Resource Not Found");
    pd.setDetail(ex.getMessage());

    return pd;
}

What ProblemDetail Actually Solves

Before Spring Boot 3:

  • Teams created custom error DTOs
  • Formats differed across services
  • Clients needed special parsing logic
  • Documentation became inconsistent

Now:

  • A uniform error structure is enforced
  • Clients can reliably parse failures
  • APIs become self-descriptive

Real Production Tips

Separate exceptions by domain

Example:

  • UserNotFoundException
  • OrderProcessingException
  • PaymentFailedException

Never expose internal messages

Bad:

SQL Error: column not found

Good:

Database error occurred

Add correlation IDs

Useful for tracing requests across microservices.


Monitor error rates

Track:

  • 4xx spikes
  • 5xx spikes

They indicate system health.


Common Interview Insight

If interviewer asks:

“Explain internal working of exception handling in Spring Boot.”

Answer:

“Spring Boot uses DispatcherServlet, which delegates to HandlerExceptionResolvers when an exception occurs. The ExceptionHandlerExceptionResolver looks for @ExceptionHandler methods in @ControllerAdvice classes. The first matching handler processes the exception and returns a structured response.”

This shows real understanding.


Conclusion

Production-ready exception handling is not optional—it is part of API design.

A good system:

  • Centralizes handling
  • Uses custom exceptions
  • Returns structured responses
  • Logs properly
  • Follows standards

See Also

4 thoughts on “Exception Handling in Spring Boot – Explained”

Leave a Comment