Unified Exception Handling in Spring Boot Using @ControllerAdvice, Assert, and Enum
This article explains how to replace repetitive try‑catch blocks in Java backend code with a unified exception handling mechanism based on Spring's @ControllerAdvice, custom Assert utilities, and error‑code enums, resulting in cleaner, more maintainable services and controllers.
In Java backend development, handling exceptions with numerous try { ... } catch { ... } finally { ... } blocks leads to redundant and hard‑to‑read code. The article contrasts this "ugly" style with a cleaner controller implementation.
Spring 3.2 introduced the @ControllerAdvice annotation, which works together with @ExceptionHandler , @InitBinder , and @ModelAttribute to provide centralized exception handling for all controllers, eliminating the need to repeat exception‑handling code in each controller class.
Defining a base controller to share exception logic is problematic because Java allows only single inheritance, and it creates tight coupling. @ControllerAdvice solves this by applying a single set of handlers to every controller without inheritance.
The goal is to remove more than 95% of explicit try‑catch statements by using Assert (assertion) methods for business validation, allowing developers to focus on core logic.
Example test code shows how Assert.notNull(user, "User not found.") provides a concise, readable alternative to manual if (user == null) { throw new IllegalArgumentException(...); } checks.
The source of Spring's Assert class is displayed, illustrating that it simply wraps an if check and throws an IllegalArgumentException when the condition fails.
A custom Assert interface is introduced, using default methods to create business‑specific exceptions via a BaseException . Two newException methods allow creation of exceptions with or without a cause.
An IResponseEnum interface defines code and message fields, and a ResponseEnum enum implements it, providing error codes such as BAD_LICENCE_TYPE(7001, "Bad licence type.") and LICENCE_NOT_FOUND(7002, "Licence not found.") . The enum is used together with the custom Assert to throw meaningful exceptions.
Service code demonstrates usage: ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence); and ResponseEnum.BAD_LICENCE_TYPE.assertNotNull(licenceTypeEnum); , removing explicit null checks.
The UnifiedExceptionHandler class (annotated with @Slf4j , @Component , @ControllerAdvice ) defines handlers for BusinessException , generic BaseException , a group of servlet‑related exceptions, binding exceptions, validation exceptions, and a catch‑all Exception . It also adapts messages based on the active Spring profile (production vs. development).
Special handling for 404 errors is described: by setting spring.mvc.throw-exception-if-no-handler-found=true and spring.resources.add-mappings=false , missing URLs trigger NoHandlerFoundException , which is then processed by handleServletException .
The article defines a unified response structure: a base BaseResponse with code and message , extended by CommonResponse (adds data ) and QueryDataResponse (adds pagination fields). Shortcut classes R and QR are introduced for concise return statements.
Testing results (illustrated with screenshots) show how the system correctly returns structured error responses for scenarios such as missing licence, invalid licence type, 404 not found, unsupported HTTP method, validation failures, and unexpected database errors.
In conclusion, combining assertion utilities, error‑code enums, and a global @ControllerAdvice handler covers most exception scenarios in typical Spring Boot services. The article notes that additional concerns like security, gateway fallback, or remote‑call failures may require separate handling.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
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.