Backend Development 10 min read

Unified Exception Handling in Spring: @ControllerAdvice, Assert, and Enum‑Based Business Exceptions

This article explains how to centralize exception handling in Spring applications using @ControllerAdvice, replace repetitive null‑checks with Assert utilities, and combine enums with custom BusinessException classes to create a clean, maintainable backend error‑handling strategy.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Unified Exception Handling in Spring: @ControllerAdvice, Assert, and Enum‑Based Business Exceptions

During software development a large amount of try‑catch and finally blocks leads to redundant code and poor readability; a unified exception handling approach can centralize error processing and improve maintainability.

What is Unified Exception Handling?

Spring 3.2 introduced the @ControllerAdvice annotation, which works with @ExceptionHandler to apply exception‑handling methods across all controllers, eliminating the need to duplicate handling logic in each controller or to create a base controller class.

Practical Implementation

Before defining a unified handler, the article shows how to replace explicit null checks with Assert utilities:

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

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

Assert.notNull internally uses the same if‑null‑throw pattern, providing a cleaner API.

Enum‑Based Business Exceptions

By defining an interface IResponseEnum with getCode() and getMessage() , a custom BusinessException can be created that carries both a code and a message. The BusinessExceptionAssert interface extends IResponseEnum and Assert , providing default methods to create exceptions from enum values:

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

public class BusinessException extends BaseException {
    private static final long serialVersionUID = 1L;
    public BusinessException(IResponseEnum responseEnum, Object[] args, String message) {
        super(responseEnum, args, message);
    }
    public BusinessException(IResponseEnum responseEnum, Object[] args, String message, Throwable cause) {
        super(responseEnum, args, message, cause);
    }
}

public interface BusinessExceptionAssert extends IResponseEnum, Assert {
    @Override
    default BaseException newException(Object... args) {
        String msg = MessageFormat.format(this.getMessage(), args);
        return new BusinessException(this, args, msg);
    }
    @Override
    default BaseException newException(Throwable t, Object... args) {
        String msg = MessageFormat.format(this.getMessage(), args);
        return new BusinessException(this, args, msg, t);
    }
}

@Getter
@AllArgsConstructor
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, developers can throw specific exceptions without creating a separate class for each case, e.g., ResponseEnum.LICENCE_NOT_FOUND.assertNotNull(licence);

Unified Response Structure

The article also defines a common response hierarchy: BaseResponse contains code and message ; CommonResponse extends it with a data field; ErrorResponse represents failure results.

Validating the Unified Handler

A sample service demonstrates how to integrate the common package, perform null checks with the enum‑based asserts, and return DTOs:

@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 Assert utilities, enum‑based BusinessException, and @ControllerAdvice enables most business exceptions to be captured and returned as a unified {code, message, data} structure, while also simplifying the addition of new error types without proliferating exception classes.

When security, gateway degradation, or remote‑call failures are involved, additional handling may be required, and internationalization should be applied to the returned messages.

backendJavaSpringenumAssertExceptionHandlingControllerAdvice
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

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.