Custom Exceptions
Building a clean exception hierarchy for enterprise applications — naming conventions, when to extend checked vs unchecked, and how Spring Boot maps them to HTTP responses.
Last reviewed: January 2025
🔍 What Is It?
Custom Exception Design:
RuntimeException (base — unchecked)
└── AppException (your base)
├── NotFoundException (404)
│ ├── UserNotFoundException
│ ├── OrderNotFoundException
│ └── ProductNotFoundException
├── ValidationException (400)
│ ├── InvalidEmailException
│ └── InsufficientFundsException
├── ConflictException (409)
│ └── DuplicateEmailException
└── ServiceException (500)
└── ExternalServiceExceptionException Fields:
- →
message— human-readable description - →
cause— original exception (for chaining) - →
errorCode— machine-readable code (for API responses) - →
context— additional data (userId, orderId, etc.)
🎯 Why Do We Need It?
Custom exceptions provide:
- →Clarity — UserNotFoundException is self-documenting; RuntimeException is not
- →Specific handling — catch UserNotFoundException separately from InsufficientFundsException
- →HTTP mapping — Spring @ControllerAdvice maps each exception type to a status code
- →Error codes — API clients can programmatically handle specific error types
- →Domain language — exceptions speak the language of your business domain
🏗️ What Happens in Real Projects?
Every production Spring Boot service has a custom exception hierarchy. The pattern:
- Define base AppException (unchecked)
- Define category exceptions (NotFoundException, ValidationException, etc.)
- Define specific exceptions (UserNotFoundException, etc.)
- Map to HTTP in @ControllerAdvice
- Throw specific exceptions in service layer
- Never let raw RuntimeException reach the controller
⚙️ How It Works
Minimal Custom Exception:
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(Long userId) {
super("User not found with ID: " + userId);
}
public UserNotFoundException(String email) {
super("User not found with email: " + email);
}
}Rich Exception with Context:
Include an error code and additional context for API responses and logging.
💻 Example
Enterprise Exception Hierarchy — A complete, production-ready exception design for a Spring Boot application.
🤔 Common Confusions
❓ Should custom exceptions extend Exception (checked) or RuntimeException (unchecked)?
In modern enterprise Java, extend RuntimeException (unchecked). This avoids forcing every caller to handle or declare the exception. The exception hierarchy handles all specific cases in @ControllerAdvice. Only use checked custom exceptions if you have a library API where callers genuinely need compiler-enforced handling.
❓ How many constructors should a custom exception have?
At minimum: (String message) and (String message, Throwable cause). The cause constructor is critical for exception chaining. Many teams also add convenience constructors for common cases, like UserNotFoundException(Long id) which formats the message automatically.
🚨 Common Production Issues
Too many exception types — having 50 specific exception classes becomes hard to maintain. Group by HTTP status category (NotFoundException, ValidationException) and use error codes for specifics.
Forgetting the cause constructor — custom exceptions without a (String, Throwable) constructor lose the original stack trace when wrapping.
✅ Best Practices
Build a hierarchy: AppException -> NotFoundException, ValidationException, ConflictException -> specific exceptions.
Always include constructors with Throwable cause for exception chaining.
Use error codes (Strings like "USER_NOT_FOUND") for machine-readable error identification.
Map exceptions to HTTP status codes in @ControllerAdvice, not in service layer.
Keep exception messages clear and actionable — include the relevant ID or value.
🏛️ Architect's Note
Design your exception hierarchy before writing services. The hierarchy drives your API error contract. Document each exception type, its HTTP status, and its error code in your API documentation. Client applications depend on these codes to handle errors programmatically. Changing error codes is a breaking API change.
💬 Frequently Asked Questions
🎯 Key Takeaways
🔗 Related Topics
🎯 Practice this topic in interviews
Core Java Interview Prep
Practice interview questions that connect directly to this technology track.
↗ Where this topic fits
Core Java Technology Guide
Return to the Core Java learning hub.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
Sources
Sources / References
Use these Core Java references to verify the concept in official docs and continue into the most relevant SplashRide pages.
Official documentation
Related SplashRide pages
SplashRide Core Java learning path
Browse the full Core Java topic collection and learning order on SplashRide.
Core Java Interview Prep
Practice Core Java interview questions and scenario-based answers.
Backend Engineer Career Path
A structured journey through Core Java, Spring Boot, production backend engineering, and interview preparation.
Full Stack Java Developer Career Path
A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.
Try Catch
Java exception handling — try-catch-finally, multi-catch, try-with-resources, and the patterns senior developers use to write resilient enterprise code.
Throws
The throws keyword, checked vs unchecked exception design, and why modern Java frameworks moved away from checked exceptions.