无法将服务注入ConstraintValidator:HV000064异常排查与解决
自定义ConstraintValidator委托EntryService校验LocalDate合法性,该校验器应用在@Entity字段上,期望在Hibernate生命周期事件(如持久化、删除前)执行校验,但抛出异常:
jakarta.validation.ValidationException: HV000064: Unable to instantiate ConstraintValidator: x.xx.xxx.util.validator.DateIsOpenValidator
校验器初始实现代码:
@RequiredArgsConstructor public class DateIsOpenValidator implements ConstraintValidator<DateIsOpen, LocalDate> { private final EntryService entryService; @Override public boolean isValid(LocalDate date, ConstraintValidatorContext context) { return entryService.validateDateIsOpen(date); } }
已尝试的方案:
- 配置
LocalValidatorFactoryBean作为默认Validator Bean:
@Bean public LocalValidatorFactoryBean validator() { return new LocalValidatorFactoryBean(); }
- 为校验器添加无参构造,但导致
EntryService为null:
public class DateIsOpenValidator implements ConstraintValidator<DateIsOpen, LocalDate> { private EntryService entryService; public DateIsOpenValidator() {} public DateIsOpenValidator(EntryService entryService) { this.entryService = entryService; } @Override public boolean isValid(LocalDate date, ConstraintValidatorContext context) { if (entryService == null) { throw new IllegalStateException("EntryService has not been initialized"); } return entryService.validateDateIsOpen(date); } }
- 配置
HibernatePropertiesCustomizer指定校验器:
@Bean @Lazy public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(final Validator validator) { return hibernateProperties -> hibernateProperties.put("jakarta.persistence.validation.factory", validator); }
现象:仅配置LocalValidatorFactoryBean无法解决问题;添加无参构造规避实例化异常但依赖无法注入;当前配置表面可行但怀疑遗漏步骤,询问如何正确注入EntryService到ConstraintValidator。
1. 让校验器被Spring容器管理
给DateIsOpenValidator添加@Component注解,确保Spring能扫描并管理该类,这是依赖注入生效的前提:
@Component @RequiredArgsConstructor public class DateIsOpenValidator implements ConstraintValidator<DateIsOpen, LocalDate> { private final EntryService entryService; @Override public boolean isValid(LocalDate date, ConstraintValidatorContext context) { return entryService.validateDateIsOpen(date); } }
若校验器不在Spring默认组件扫描路径下,需手动通过@Bean将其注册到容器中。
2. 正确配置LocalValidatorFactoryBean
需要让LocalValidatorFactoryBean使用Spring的AutowireCapableBeanFactory创建校验器实例,以此支持依赖注入。修改Validator配置:
@Bean public LocalValidatorFactoryBean validator(AutowireCapableBeanFactory beanFactory) { LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); validator.setConstraintValidatorFactory(new SpringConstraintValidatorFactory(beanFactory)); return validator; }
3. 修正HibernatePropertiesCustomizer配置
确保Hibernate使用Spring提供的Validator实例,修正原配置的语法问题:
@Bean @Lazy public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(Validator validator) { return hibernateProperties -> hibernateProperties.put("jakarta.persistence.validation.factory", validator); }
4. 确认校验注解的正确性
确保@DateIsOpen注解指定了正确的校验器类:
@Target({FIELD, METHOD, PARAMETER}) @Retention(RUNTIME) @Constraint(validatedBy = DateIsOpenValidator.class) public @interface DateIsOpen { String message() default "日期不合法"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
关键注意事项
- 无需给校验器添加无参构造,
@RequiredArgsConstructor生成的带参构造会被Spring用来完成依赖注入,前提是校验器已被Spring管理。 - 确保
EntryService是Spring管理的Bean(如添加了@Service注解),否则无法被注入到校验器中。 - Spring Boot默认会自动配置
LocalValidatorFactoryBean,但如果自定义了Validator Bean,必须确保配置正确关联Spring的Bean工厂。
内容的提问来源于stack exchange,提问作者Jasper Lutz Severino

