Spring Boot拆分Validator声明与实现后Hibernate校验异常求助
这个报错java.lang.NoSuchMethodException: test.UniqueUsernameValidator.<init>()的根源很明确:Hibernate Validation默认会尝试直接实例化你在@Constraint(validatedBy)中指定的类,但你这里填的是接口,接口没有构造方法,自然无法实例化。
你的思路是对的——通过自定义ConstraintValidatorFactory让Spring来管理Validator实例(包括接口的实现类),但问题出在Hibernate还没用上这个自定义Factory。下面是具体的修复步骤:
1. 优化自定义Factory的Spring管理方式
你的SpringConstraintValidatorFactoryEx已经是@Component,但可以改成更简洁的构造注入,避免手动传参的潜在问题:
@Component @Slf4j public class SpringConstraintValidatorFactoryEx implements ConstraintValidatorFactory { private final AutowireCapableBeanFactory beanFactory; // 让Spring自动注入AutowireCapableBeanFactory,无需手动传入 public SpringConstraintValidatorFactoryEx(AutowireCapableBeanFactory beanFactory) { this.beanFactory = beanFactory; } @Override public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) { T bean = null; try { log.info("Trying to find validator bean for type {}", key.getSimpleName()); // 按类型查找Bean,会自动匹配接口的实现类 bean = this.beanFactory.getBean(key); } catch (BeansException exc) { log.info("Failed to find bean for type {}: {}", key.getSimpleName(), exc.getMessage()); } if (bean == null) { try { log.info("Creating new validator instance for type {}", key.getSimpleName()); bean = this.beanFactory.createBean(key); } catch (BeansException exc) { log.info("Failed to create validator for type {}", key.getSimpleName()); } } if (bean == null) { log.warn("No validator found for type {}", key.getSimpleName()); } return bean; } @Override public void releaseInstance(ConstraintValidator<?, ?> instance) {} }
2. 配置全局Validator Bean,强制Hibernate使用自定义Factory
修改你的Validator配置Bean,用依赖注入获取自定义Factory,并标记为@Primary确保它是全局默认的Validator(覆盖Spring Boot的默认实例,包括Hibernate使用的):
@Configuration public class ValidationConfig { @Bean @Primary public Validator validator(SpringConstraintValidatorFactoryEx constraintValidatorFactory) { LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); // 绑定自定义Factory,让所有校验请求都通过Spring获取Validator实例 validator.setConstraintValidatorFactory(constraintValidatorFactory); validator.afterPropertiesSet(); return validator; } }
3. 强制Hibernate使用Spring的Validator(可选保险步骤)
如果上面的配置后Hibernate还是自己初始化校验器,可以在application.properties中添加配置,指定JPA使用Spring的校验工厂:
spring.jpa.properties.javax.persistence.validation.factory=org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
为什么之前的配置没生效?
你之前手动创建SpringConstraintValidatorFactoryEx并传入wac.getAutowireCapableBeanFactory()本身没问题,但可能存在以下情况:
- 你的
ValidatorBean没有被标记为@Primary,Spring Boot可能使用了默认的Validator实例 - Hibernate在触发JPA实体校验时,没有使用Spring管理的Validator,而是自己初始化了默认的校验器
通过上面的步骤,我们确保了整个应用(包括Spring MVC和Hibernate JPA)都使用同一个由Spring管理的Validator实例,而这个Validator会通过你的自定义Factory去查找接口对应的实现类,完美解决拆分声明与实现的需求。
内容的提问来源于stack exchange,提问作者zolv

