Spring自定义Validator未自动执行?排查错误原因及解决方法
问题:Spring Boot自定义Validator未自动执行,条件验证失效
我需要验证POST请求的JSON内容,实现当属性type为A且name为空时输入无效的条件验证。使用自定义Validator实现,但该Validator未被执行,只有在控制器中通过@InitBinder手动注册才能生效,希望找到更优雅的解决方式。
现有代码
控制器代码
@RestController @RequestMapping("/orders") @Validated public class OrderController { @PostMapping @ResponseStatus(HttpStatus.CREATED) public void createOrder(@Valid @RequestBody OrderDto order) { // 业务逻辑 } }
自定义Validator代码
@Component public class OrderValidator implements Validator { @Override public boolean supports(Class<?> clazz) { return OrderDto.class.isAssignableFrom(clazz); } @Override public void validate(Object target, Errors errors) { final var order = (OrderDto) target; if (order.getType() == Type.A && Strings.isEmpty(order.getName())) { errors.rejectValue("name", "name.required", "Name required for type A"); } } }
OrderDto实体类
@Getter @Setter public class OrderDto { private String name; @NotNull private Type type; }
问题现象
- 当
type为null时,@NotNull注解正常触发,返回HTTP 400错误; - 当
type为A且name为null时,自定义的OrderValidator未执行(断点未进入supports和validate方法),请求返回HTTP 201成功; - Validator已被Spring Boot识别为Bean,可以正常注入。
原因分析
Spring Boot默认不会自动将实现Validator接口的Bean关联到对应的实体类上,@Valid注解只会触发JSR-380标准的Bean Validation注解(如@NotNull),不会自动调用自定义的Validator实现,必须通过显式注册才能生效。
解决方案
方案一:全局注册自定义Validator
通过实现WebMvcConfigurer全局配置,将自定义Validator注册到Spring的验证体系中,无需在每个控制器中添加@InitBinder:
@Configuration public class WebValidationConfig implements WebMvcConfigurer { private final OrderValidator orderValidator; // 构造方法注入Validator public WebValidationConfig(OrderValidator orderValidator) { this.orderValidator = orderValidator; } @Override public void addValidators(ValidatorRegistry registry) { registry.addValidator(orderValidator); } }
方案二:改用JSR-380自定义注解(推荐)
遵循Bean Validation标准,通过自定义注解+ConstraintValidator实现条件验证,这种方式更符合Spring Boot的验证体系,无需额外注册:
- 定义自定义约束注解
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = OrderTypeConstraintValidator.class) public @interface ValidOrder { String message() default "Name is required when type is A"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
- 实现约束验证器
public class OrderTypeConstraintValidator implements ConstraintValidator<ValidOrder, OrderDto> { @Override public boolean isValid(OrderDto order, ConstraintValidatorContext context) { // 当type为A时,name不能为空 if (Type.A.equals(order.getType())) { return !Strings.isEmpty(order.getName()); } // 其他类型无需验证name return true; } }
- 在实体类上添加注解
@Getter @Setter @ValidOrder // 添加自定义验证注解 public class OrderDto { private String name; @NotNull private Type type; }
完成以上配置后,原控制器代码无需修改,@Valid注解会自动触发@ValidOrder对应的验证逻辑,当type为A且name为空时,会返回HTTP 400错误。
内容的提问来源于stack exchange,提问作者Datz
相关产品推荐
相关产品推荐

