动态表单校验通过分组注解(如insertstep、reviewstep、archivestep)声明字段所处流程阶段,controller按接口绑定对应分组实现按需校验;结合@statusflow注解约束状态流转,并用bindingresult精准定位错误字段。

动态表单校验中,不靠硬编码 if-else,而是用位置感知的注解驱动流程控制,关键在于把“字段在哪一阶段可用”这个业务逻辑,直接声明在 DTO 字段上。
用分组注解(Groups)标记字段所处流程位置
Spring Validation 的 @Validated 支持按组触发校验,每个组可对应一个流程节点(如“创建中”“审核中”“归档后”)。你不需要写 switch 或状态机,只需定义几个空接口作为分组标识:
- InsertStep.class:仅在新建提交时校验
- ReviewStep.class:仅在审核环节生效
- ArchiveStep.class:仅在归档动作中启用
然后在 DTO 中按字段所处环节标注分组:
public class DynamicFormDTO {@NotBlank(groups = InsertStep.class)
private String title;
@NotNull(groups = ReviewStep.class)
@Min(value = 1, groups = ReviewStep.class)
private Integer reviewerId;
@Pattern(regexp = "APPROVED|REJECTED", groups = ArchiveStep.class)
private String decision;
}
Controller 层按动作绑定对应分组
不同接口调用不同分组,框架自动跳过非本组字段:
- POST /form/create → @Validated(InsertStep.class)
- PUT /form/{id}/review → @Validated(ReviewStep.class)
- POST /form/{id}/archive → @Validated(ArchiveStep.class)
这样同一个 DTO 类,在不同请求路径下只校验它“该出现”的字段,无需复制类或写条件判断。
结合自定义注解实现状态流转约束
如果字段校验还依赖前置状态(比如“只有当前状态是 DRAFT 才允许改 title”),可复用知识库中提到的 @StatusFlow 注解:
- 定义注解时指定
from = {1}(DRAFT 状态码)、to = 2(PUBLISHED) - 验证器内部读取 DTO 中的
currentStatus字段,比对是否符合流转规则 - 校验失败直接抛出带提示的 ConstraintViolation
它和分组注解不冲突,可同时使用:一个管“字段何时参与校验”,一个管“字段变更是否合法”。
抽取字段时用 BindingResult 精准定位
校验失败后,不遍历全部字段。通过 BindingResult 可直接拿到本次触发校验的字段名与错误信息:
if (bindingResult.hasErrors()) {FieldError error = bindingResult.getFieldError();
String fieldName = error.getField(); // 如 "reviewerId"
String message = error.getDefaultMessage(); // 如 "不能为null"
// 前端可据此高亮对应表单项
}
配合前端动态渲染的 fieldKey,实现错误精准回填,无需约定字段顺序或索引。











