Backend Development 11 min read

Unified Exception Handling in Spring Using @ControllerAdvice and Custom Assertions

This article explains how to replace repetitive try‑catch blocks in Spring applications with a unified exception handling mechanism based on @ControllerAdvice, custom assertion utilities, and enum‑driven business exceptions, while also showing how to standardize response structures and integrate internationalization.

Top Architect
Top Architect
Top Architect
Unified Exception Handling in Spring Using @ControllerAdvice and Custom Assertions

Introduction

During software development a large amount of time is spent handling exceptions, which leads to many repetitive try {…} catch {…} finally {…} blocks that clutter code and reduce readability.

What Is Unified Exception Handling?

Since Spring 3.2 the @ControllerAdvice annotation can be combined with @ExceptionHandler , @InitBinder , and @ModelAttribute to apply exception‑handling logic across all controllers without coupling to each controller class.

Using @ControllerAdvice , a single class can define handling methods for various exception types, eliminating the need to duplicate handlers in every controller or to create a base controller class that would increase coupling.

Practical Implementation

Before defining a unified exception handler, the article recommends using assertions instead of explicit throw new Exception statements. Example test methods demonstrate Assert.notNull(user, "User does not exist.") and its underlying implementation that throws an IllegalArgumentException when the condition fails.

@Test
public void test1() {
    ...
    User user = userDao.selectById(userId);
    Assert.notNull(user, "用户不存在.");
    ...
}

@Test
public void test2() {
    // another style
    User user = userDao.selectById(userId);
    if (user == null) {
        throw new IllegalArgumentException("用户不存在.");
    }
}

The article then shows how to create a custom assertion interface that returns a domain‑specific exception via a newException method.

default void assertNotNull(Object obj, Object... args) {
    if (obj == null) {
        throw newException(args);
    }
}

Enum‑Based Business Exceptions

Define an interface IResponseEnum with getCode() and getMessage() . Implement a BusinessException that extends a generic BaseException . Then create an enum (e.g., ResponseEnum ) that implements a combined BusinessExceptionAssert interface, providing concrete enum constants such as BAD_LICENCE_TYPE and LICENCE_NOT_FOUND each carrying a code and message.

public interface IResponseEnum {
    int getCode();
    String getMessage();
}

public class BusinessException extends BaseException { ... }

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;
    // getters omitted
}

Using this enum, validation methods become concise, e.g., ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence); .

Unified Response Structure

Define a BaseResponse containing code and message . Extend it with CommonResponse (adds data ) and ErrorResponse for failure cases, ensuring every API returns a consistent payload.

Validation of the Unified Handler

Integrate the common package into new projects. Example service code shows how to use the assertions and enum‑driven exceptions in typical CRUD methods, including pagination, creation, and existence checks.

@Service
public class LicenceService extends ServiceImpl
{
    @Autowired
    private OrganizationClient organizationClient;

    public LicenceDTO queryDetail(Long licenceId) {
        Licence licence = this.getById(licenceId);
        checkNotNull(licence);
        OrganizationDTO org = ClientUtil.execute(() -> organizationClient.getOrganization(licence.getOrganizationId()));
        return toLicenceDTO(licence, org);
    }

    private void checkNotNull(Licence licence) {
        ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence);
    }
    // other methods omitted for brevity
}

Conclusion

Combining assertions, enum‑based business exceptions, and @ControllerAdvice enables most exceptions to be captured and returned as a uniform code / message structure, improving code readability and maintainability. The article also notes that additional exception types (security, gateway, remote‑call failures) may require separate handling, and that internationalization should be applied to error messages before returning them to clients.

backendjavaspringenumAssertionsExceptionHandlingControllerAdvice
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.