How to Use SpEL Validator for Complex Parameter Validation in ContiNew Admin

The SpEL Validator extends Jakarta Bean Validation by letting you write Spring Expression Language conditions that determine when standard constraints like @NotBlank should be applied, enabling field-level validation that depends on other field values or enum constants.

ContiNew Admin implements a sophisticated validation layer using SpEL (Spring Expression Language) to handle complex, conditional validation rules that standard Jakarta annotations cannot express alone. This article explains how to leverage the SpEL Validator for complex parameter validation using real examples from the continew-org/continew-admin repository source code.

How SpEL Validation Works in ContiNew Admin

The SpEL Validator integrates with Spring Boot’s validation infrastructure through four core components defined in the continew-system module:

  • @SpelValid – Marks a request DTO as a candidate for SpEL-based validation, processed alongside standard @Valid annotations. Found in top/continew/admin/system/model/req/StorageReq.java.
  • SpEL constraint annotations (@SpelNotBlank, @SpelNotEmpty, @SpelNotNull, @SpelFuture) – Define a SpEL condition that must evaluate to true for the associated Jakarta constraint to be enforced. The expression can reference the current object via #this and static members using T(...). Located in files like NoticeReq.java.
  • ValidationGroup – Provides validation groups (e.g., ValidationGroup.Storage.OSS) for applying constraints only in specific contexts, such as particular storage types. Defined in ValidationGroup.java.
  • Validator.validate() – Triggers the full validation chain including SpEL constraints. Used in AbstractLoginHandler.java at line 82.

When a DTO annotated with @SpelValid is validated, the infrastructure evaluates each SpEL expression; if the condition returns true, the associated constraint is applied, otherwise the field is skipped.

Conditional Field Validation Examples

Validating Fields Based on Boolean Flags

In StorageReq.java (lines 122–127), the recycleBinPath field is required only when the recycle bin feature is enabled:

@SpelNotBlank(
    condition = "#this.recycleBinEnabled == true",
    message = "回收站路径不能为空"
)
private String recycleBinPath;

When recycleBinEnabled is true, the field must be non-blank; when false, the constraint is ignored entirely.

Enum-Based Conditional Validation

The NoticeReq.java file (lines 80–86) demonstrates validating a list based on an enum value:

@SpelNotEmpty(
    condition = "#this.noticeScope == T(top.continew.admin.system.enums.NoticeScopeEnum).USER",
    message = "通知用户不能为空"
)
private List<String> noticeUsers;

Here, noticeUsers must contain at least one element only when noticeScope equals the USER enum constant.

Temporal Constraints with Conditions

For scheduled publishing in NoticeReq.java (lines 100–106), two constraints apply only when timing is enabled:

@SpelNotNull(
    condition = "#this.isTiming == true",
    message = "定时发布时间不能为空"
)
@SpelFuture(
    condition = "#this.isTiming == true",
    message = "定时发布时间不能早于当前时间"
)
private LocalDateTime publishTime;

Both the non-null check and future-date validation are skipped unless isTiming is true.

Inverse Boolean Conditions

In ClientReq.java (lines 88–92), validation applies when a feature is disabled:

@SpelNotNull(
    condition = "#this.isConcurrent == false",
    message = "顶人下线的范围无效"
)
private ReplacedRangeEnum replacedRange;

The replacedRange field is required only when concurrent login (isConcurrent) is disabled.

Validation Groups for Type-Specific Rules

For scenarios requiring entirely different validation rules based on storage types, ContiNew Admin uses validation groups. In StorageReq.java, the accessKey field is validated only for OSS storage:

@NotBlank(message = "Access Key不能为空", groups = ValidationGroup.Storage.OSS.class)
private String accessKey;

The groups are defined in ValidationGroup.java. To trigger group-specific validation, call:

Validator.validate(req, ValidationGroup.Storage.OSS.class);

This pattern appears in AbstractLoginHandler.java at line 82, where the generic validation entry point processes both standard and SpEL constraints.

Summary

  • SpEL annotations (@SpelNotBlank, @SpelNotEmpty, etc.) wrap standard Jakarta constraints with conditional logic using the condition attribute.
  • The ** #this ** variable references the current object being validated, allowing cross-field dependencies.
  • Static references use T(full.class.name).CONSTANT syntax to compare against enum values or constants.
  • Validation groups defined in ValidationGroup.java enable schema-level validation switching for different operational modes.
  • Validation is triggered centrally via Validator.validate() as implemented in AbstractLoginHandler.java.

Frequently Asked Questions

What is the difference between @SpelValid and standard @Valid?

@SpelValid is a marker annotation that signals the validation engine to process SpEL constraint annotations on the DTO, while @Valid triggers standard Jakarta validation. In ContiNew Admin, @SpelValid works alongside @Valid to enable conditional constraints without replacing the standard validation flow.

Can I reference other fields in the same DTO from a SpEL condition?

Yes. Use the #this variable to reference the current object and access other fields, as shown in StorageReq.java where #this.recycleBinEnabled controls the validation of recycleBinPath. You can chain property access (e.g., #this.parent.child.value) for nested dependencies.

How do I validate a field only for specific enum values?

Use the T() operator to reference the enum class and compare against constants. In NoticeReq.java, the expression T(top.continew.admin.system.enums.NoticeScopeEnum).USER validates noticeUsers only when the scope is set to USER.

Where is the validation actually triggered in the codebase?

The central validation trigger is Validator.validate(req) found in AbstractLoginHandler.java at line 82. This utility method invokes the Spring Validator implementation that processes both standard Jakarta constraints and SpEL conditions defined in the request DTOs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →