4.5 Custom Exceptions & Enterprise Best Practices
← Back to Chapter 4: Exception Handling
In production enterprise architectures, standard library exceptions like IllegalArgumentException are often too generic to communicate specific business-domain failures.
Why use custom exceptions?
Instead of:
RuntimeException
you can clearly communicate:
UserNotFoundException
PaymentFailedException
OrderNotFoundException
This makes code:
- easier to understand
- easier to maintain
- easier to debug
- easier to handle differently
Custom checked vs unchecked exception
You can create both.
Checked custom exception
Exception
class MyException extends Exception {
}
Caller must handle or declare it.
Unchecked custom exception
RuntimeException
class MyException extends RuntimeException {
}
Caller isn't forced by compiler to handle it.
Which one is usually preferred in Spring Boot?
For business/application exceptions, unchecked exceptions extending RuntimeException are commonly preferred.
Why we should not use Exception everywhere?
You might see:
try {
// code
} catch (Exception e) {
// do something
}
This is usually not a good practice.
Why?
Because it catches almost everything under Exception and can hide the actual problem.
Better:
catch (UserNotFoundException e)
or:
catch (SQLException e)
Why @RestControllerAdvice?
It allows us to define exception handling in one central location instead of writing:
try-catch
inside every controller method.
Without global handling:
Controller 1 → try/catch
Controller 2 → try/catch
Controller 3 → try/catch
Controller 4 → try/catch
Messy.
With global handling:
Global Exception Handler
/ | \
/ | \
Controller 1 Controller 2 Controller 3
Much cleaner.
@ExceptionHandler
Inside a global exception handler, we commonly use:
@ExceptionHandler
It tells Spring:
"When this particular exception occurs, use this method to generate the response."
For example:
UserNotFoundException
↓
@ExceptionHandler(UserNotFoundException.class)
↓
HTTP 404 response