如何让@NotNull注解在自定义校验器MyDtoConstraint前执行?
问题原因与解决方案
为什么自定义校验器会先执行?
核心问题出在组序列的触发逻辑和嵌套校验的时机:
- 你给
MyDto定义的组序列是LightErrors → HeavyError → 默认组,但MyDto本身没有属于LightErrors组的直接约束,只有类级的MyDtoConstraint属于HeavyError组。 - 校验器处理
MyDto的LightErrors组时,因为当前对象没有该组的约束,会直接跳过这个组,进入HeavyError组的处理。 - 在
HeavyError组阶段,校验器先执行类级的MyDtoConstraintValidator(此时MyDto的HeavyError组没有属性级约束,直接走类级校验),之后才会触发commission对象的HeavyError组校验——但commission的@NotNull属于LightErrors组,根本不会在这个阶段执行。 - 只有当校验器处理到
MyDto的默认组时,才会按照MyCommissionDto的组序列执行它的LightErrors组约束,但这已经在自定义校验器执行之后了。
简单说:你的组序列逻辑没触发嵌套对象的LightErrors组校验,导致@NotNull被延迟到默认组阶段,而自定义校验器在HeavyError组提前执行了。
如何让@NotNull先执行?
有两种可靠的解决方式:
方案1:给MyDto的commission属性加LightErrors组的空约束
在MyDto的commission属性上添加一个属于LightErrors组的无实际校验逻辑的约束,目的是让校验器在处理LightErrors组时,触发嵌套对象的同组校验:
@GroupSequence(value = {ValidationGroup.LightErrors.class, ValidationGroup.HeavyError.class, MyDto.class}) @MyDtoConstraint(groups = ValidationGroup.HeavyError.class) public class MyDto { @Valid @NotNull(groups = ValidationGroup.LightErrors.class, message = "") // 仅用于触发LightErrors组的嵌套校验 private MyCommissionDto commission; }
这样,当MyDto的LightErrors组被处理时,会触发commission的LightErrors组校验,先执行@NotNull(type),之后才进入HeavyError组执行自定义校验器。
方案2:显式指定校验组为LightErrors和HeavyError
在Spring的校验入口(比如Controller)显式指定要校验的组,让组序列严格按顺序执行:
@PostMapping("/submit") public ResponseEntity<?> submit( @Validated({ValidationGroup.LightErrors.class, ValidationGroup.HeavyError.class}) @RequestBody MyDto dto ) { // 业务逻辑 return ResponseEntity.ok("success"); }
此时校验器会先完整执行LightErrors组的所有约束(包括嵌套的MyCommissionDto的@NotNull),确认无错后才会执行HeavyError组的自定义校验器。
补充说明
- 组序列的核心是前一组所有约束通过后,才执行下一组,但前提是每个组都有对应的触发点(当前对象的约束或
@Valid嵌套的同组约束)。 - 嵌套对象的组序列只作用于自身校验,不影响父对象的组执行顺序。
内容的提问来源于stack exchange,提问作者Eugene_Z
相关产品推荐
相关产品推荐

