Custom Exception Classes in Spring Boot — ResourceNotFoundException Pattern
Fast recall
Quick revision
Core answer
Understanding the full exception flow in a layered Spring Boot application from service to controller to client
Returning null from service methods instead of throwing exceptions, forcing null checks everywhere and making the API contract unclear
Show the complete flow: custom exception class → thrown in service → caught in @ControllerAdvice → structured HTTP response with correct status code
Interview-ready guide
Interview answer
For example, OrderService can throw new OrderNotFoundException(orderId). A @RestControllerAdvice handler maps it to a 404 ProblemDetail without exposing a repository exception. DuplicateOrderNumberException can map to 409. Invalid JSON and Bean Validation failures are framework/boundary failures and need not be forced into the same custom class.
Custom Exception Classes in Spring Boot — ResourceNotFoundException Pattern
Direct Answer
A custom exception gives a failure a precise domain name and useful context. RuntimeException is common for service/domain failures that callers are not expected to recover from locally, but checked versus unchecked is a design decision, not a Spring Boot requirement. Keep HTTP mapping at the web boundary with @ExceptionHandler/@ControllerAdvice or another error adapter.
Code Example
// Custom exception class
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
// Service layer - throw the exception
@Service
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public Order getOrderById(Long id) {
return orderRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Order not found with id: " + id));
}
}
// Global handler - catch and convert to HTTP response
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex, HttpServletRequest request) {
var error = new ErrorResponse(404, ex.getMessage(), LocalDateTime.now(), request.getRequestURI());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
}
record ErrorResponse(int status, String message, LocalDateTime timestamp, String path) {}
Common Mistakes & Pitfalls
- Returning null instead of throwing: Freshers often return null from service methods when a resource isn't found, forcing every caller to check for null and leading to NullPointerExceptions in controllers — throw ResourceNotFoundException instead to fail fast with a clear error.
- Extending Exception instead of RuntimeException: Creating checked exceptions forces try-catch blocks everywhere, cluttering code and violating Spring's fail-fast philosophy for domain errors.
- Throwing exceptions from controllers: Controllers should never throw domain exceptions directly — they should call services which throw them, maintaining clean separation of concerns.
- Creating too many exception types: Having 15 different not-found exceptions (UserNotFoundException, OrderNotFoundException, etc.) creates maintenance overhead — use one ResourceNotFoundException with a descriptive message parameter instead.
Check your understanding
Answer these without looking back at the article.
- Why extend RuntimeException instead of Exception for custom exceptions?
- What is the difference between checked and unchecked exceptions in Java?
- How do you return a structured error DTO from @ControllerAdvice?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.