Spring Boot 3中为标准Bean Validation注解注册自定义验证器失败求助
在Spring Boot 3.0.4中遇到以下错误:
HV000030: No validator could be found for constraint 'jakarta.validation.constraints.Size' validating type 'com.example.entity.dataType.GenericString'. Check configuration for 'iso3'
已尝试多种方案但毫无进展:
- 尝试注册自定义约束验证器但未成功
- 尝试容器元素验证方案同样失败
- 尝试结合自定义验证器与现有通用验证器的方式,始终无法生效
相关源码如下:
实体类
public class Test { @Id @Column(name = "id", nullable = false) private long id; @Randomizer(GenericStringValidator.class) @Convert(converter = GenericStringConverter.class) @JsonSerialize(using = GenericStringSerializer.class) @Column(name = "iso3", nullable = false, length = 3) @Size(min = 3, max = 3) private GenericString iso3; }
值提取器
@UnwrapByDefault public class GenericStringValueExtractor implements ValueExtractor<@ExtractedValue(type = String.class) GenericString> { @Override public void extractValues(GenericString originalValue, ValueExtractor.ValueReceiver receiver) { receiver.value(null, originalValue.getValue()); } }
@Size验证器实现类
@Component public class GenericStringSizeConstraintValidator implements ConstraintValidator<Size, GenericString> { private int min; private int max; @Override public void initialize(Size parameters) { this.min = parameters.min(); this.max = parameters.max(); try { this.validateParameters(); } catch (Exception e) { throw new RuntimeException(e); } } private boolean check(String value) { if (value == null) { return true; } else { int length = value.length(); return length >= this.min && length <= this.max; } } public boolean isValid(GenericString dataType, ConstraintValidatorContext constraintValidatorContext) { if (dataType == null) { return true; } else { return check(((FrameworkDataType<String>) dataType).getValue()); } } private void validateParameters() throws Exception { if (this.min < 0) { throw new Exception("Min Value cannot be nagative"); } else if (this.max < 0) { throw new Exception("Max Value cannot be nagative"); } else if (this.max < this.min) { throw new Exception("Length Value cannot be nagative"); } } }
自定义ValidatorFactoryBean
@Component public class ValidatorFactoryBean extends LocalValidatorFactoryBean { @Autowired private GenericStringSizeConstraintValidator genericStringSizeConstraintValidator; @Override protected void postProcessConfiguration(Configuration<?> configuration) { super.postProcessConfiguration(configuration); configuration.addValueExtractor(new GenericStringSizeConstraintValidator()); configuration.addValueExtractor(new GenericNameSizeConstraintValidator()); HibernateValidatorConfiguration configurationH = Validation.byProvider(HibernateValidator.class) .configure(); ConstraintMapping constraintMapping = configurationH.createConstraintMapping(); constraintMapping.constraintDefinition(Size.class) .includeExistingValidators(true) .validatedBy(GenericStringSizeConstraintValidator.class); configurationH.addMapping(constraintMapping); } }
编辑1:找到相关文章,但创建META-INF/services/javax.validation.ConstraintValidator文件的方案在Spring Boot 3中无效。
你的配置存在几个关键问题,导致自定义验证器无法被正确识别:
1. 修复值提取器注册错误
你错误地将验证器实例当作值提取器注册,应该注册的是GenericStringValueExtractor,而不是GenericStringSizeConstraintValidator。修正代码:
// 替换原有错误的注册逻辑 configuration.addValueExtractor(new GenericStringValueExtractor());
2. 关联约束映射到当前工厂配置
你创建了新的HibernateValidatorConfiguration实例但未关联到当前LocalValidatorFactoryBean的配置。正确做法是将约束映射添加到传入的configuration对象:
@Override protected void postProcessConfiguration(Configuration<?> configuration) { super.postProcessConfiguration(configuration); // 注册正确的值提取器 configuration.addValueExtractor(new GenericStringValueExtractor()); // 强转为HibernateValidatorConfiguration HibernateValidatorConfiguration hibernateConfig = (HibernateValidatorConfiguration) configuration; ConstraintMapping constraintMapping = hibernateConfig.createConstraintMapping(); // 为Size约束添加自定义验证器,保留原有验证器 constraintMapping.constraintDefinition(Size.class) .includeExistingValidators(true) .validatedBy(GenericStringSizeConstraintValidator.class); // 将映射添加到当前配置 hibernateConfig.addMapping(constraintMapping); }
3. 修正ServiceLoader文件路径(可选)
Spring Boot 3使用Jakarta EE API,ServiceLoader文件路径应为META-INF/services/jakarta.validation.ConstraintValidator,而非javax.validation.ConstraintValidator。文件内容添加你的验证器全类名:
com.example.xxx.GenericStringSizeConstraintValidator
4. 移除验证器的@Component注解
若通过代码或ServiceLoader注册验证器,无需给GenericStringSizeConstraintValidator添加@Component注解,避免Spring重复管理实例。
验证生效
启动应用后,触发Test实体的验证(如通过@Valid绑定请求参数或直接调用Validator.validate()),检查错误是否消失。
内容的提问来源于stack exchange,提问作者Burner

