Unified Exception Handling in Spring: Using @ControllerAdvice, Assert, and Enum for Clean Code
This article explains how to replace repetitive try‑catch blocks in Java Spring applications with a unified exception handling mechanism that leverages @ControllerAdvice, custom Assert utilities, and an enum‑based error‑code system to produce clean, maintainable backend code.
Background – In typical Java projects a large amount of boilerplate try { … } catch { … } finally { … } code appears, especially in Controllers and Services, which hurts readability.
The author compares an ugly try‑catch style with a clean Controller design and argues for moving exception handling out of business code.
What is Unified Exception Handling?
Since Spring 3.2 the @ControllerAdvice annotation can be combined with @ExceptionHandler, @InitBinder, and @ModelAttribute to apply exception handling globally.
Only @ExceptionHandler actually deals with exceptions. By defining a single class annotated with @ControllerAdvice, you can catch all controller‑level and service‑level exceptions in one place.
Goal
Eliminate more than 95% of explicit try‑catch blocks by using an Assert (assertion) style that throws custom business exceptions, allowing developers to focus on business logic.
Practical Implementation
1. Assert Replacement – Use Spring's org.springframework.util.Assert (or a custom one) to replace manual if (obj == null) { throw new … } checks.
@Test
public void test1() {
User user = userDao.selectById(userId);
Assert.notNull(user, "User does not exist.");
}
public void test2() {
User user = userDao.selectById(userId);
if (user == null) {
throw new IllegalArgumentException("User does not exist.");
}
}The custom Assert interface defines newException methods so that the thrown exception can be a domain‑specific BusinessException with an error code and message.
public interface Assert {
BaseException newException(Object... args);
BaseException newException(Throwable t, Object... args);
default void assertNotNull(Object obj) {
if (obj == null) {
throw newException(obj);
}
}
default void assertNotNull(Object obj, Object... args) {
if (obj == null) {
throw newException(args);
}
}
}Business exceptions are defined by an enum that implements BusinessExceptionAssert (which extends IResponseEnum and Assert), e.g.:
public enum ResponseEnum implements BusinessExceptionAssert {
BAD_LICENCE_TYPE(7001, "Bad licence type."),
LICENCE_NOT_FOUND(7002, "Licence not found.");
private int code;
private String message;
}Using the enum you can write concise validation code such as:
ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence);
ResponseEnum.BAD_LICENCE_TYPE.assertNotNull(licenceTypeEnum);Unified Exception Handler Class
The UnifiedExceptionHandler class is annotated with @ControllerAdvice and defines several @ExceptionHandler methods: handleBusinessException – catches BusinessException. handleBaseException – catches other custom BaseException s. handleServletException – catches framework exceptions such as NoHandlerFoundException, HttpRequestMethodNotSupportedException, etc., and maps them to a generic error code in production. handleBindException and handleValidException – process parameter‑binding and validation errors, aggregating field messages. handleException – a fallback for any unknown exception, returning a generic server‑error response (or a user‑friendly "Network error" in production).
Each handler logs the exception, obtains a localized message via UnifiedMessageSource, and returns an ErrorResponse containing code and message.
Exception Classification
Exceptions are divided into two major groups:
Pre‑Controller exceptions (e.g., 404, method not allowed, missing parameters) handled by handleServletException.
Service‑level exceptions – custom business exceptions handled by handleBusinessException / handleBaseException and unknown exceptions handled by handleException.
Testing the Mechanism
Various scenarios are demonstrated: missing licence, invalid licence type, 404, unsupported HTTP method, parameter validation failures, and database errors caused by a mismatched entity field. All cases return a JSON payload with code and message.
Conclusion
By combining assertions, an enum‑based error‑code system, and a global @ControllerAdvice handler, most exceptions become easy to manage, the code stays clean, and the API returns consistent error structures. The approach can be packaged as a common library and reused across projects.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
