ConstraintValidator中@Autowired注入为空的问题解决求助
解决Hibernate Validator自定义校验器无法注入Spring Bean的问题
问题背景
需要实现跨多个DTO类的字段校验自定义注解,校验规则基于正则表达式,所有规则存储在配置服务器的JSON字符串中,通过@Value注解读取。使用ConstraintValidator实现自定义校验器后,发现校验器中@Autowired注入的ValidatorConfiguration始终为null。原因是ConstraintValidator由Hibernate而非Spring管理,无法直接访问Spring托管Bean。使用版本:org.hibernate.validator 6.2.5、Spring 5.3.25。
相关代码
自定义注解类
@Target({ ElementType.FIELD }) @Retention( RetentionPolicy.RUNTIME ) @Constraint(validatedBy = DynamicRegexValidator.class) public @interface RegexValidatedField { String fieldName(); String message() default "Invalid field"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
ConstraintValidator实现类
public class DynamicRegexValidator implements ConstraintValidator<RegexValidatedField, String> { @Autowired private ValidatorConfiguration validatorConfiguration; private String fieldName; @Override public void initialize(RegexValidatedField constraintAnnotation) { this.fieldName = constraintAnnotation.fieldName(); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { ValidationRule rule = validatorConfiguration.getRuleForField(fieldName); if (rule == null || value == null) { return true; } if (!value.matches(rule.getRegex())) { return false; } return true; } }
配置类
@Configuration public class ValidatorConfiguration { @Value("${validation.rules:}") private String validationRulesJson; private Map<String, ValidationRule> validationRules; @PostConstruct public void init() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); validationRules = objectMapper.readValue(validationRulesJson, new TypeReference<Map<String, ValidationRule>>() {}); } public ValidationRule getRuleForField(String fieldName) { return validationRules.get(fieldName); } }
解决方案
方案一:通过Spring上下文静态获取Bean
创建Spring上下文工具类,用于静态获取Spring托管Bean:
@Component public class SpringContextHolder implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext context) throws BeansException { SpringContextHolder.applicationContext = context; } public static <T> T getBean(Class<T> clazz) { return applicationContext.getBean(clazz); } }
修改DynamicRegexValidator,在初始化阶段从上下文获取ValidatorConfiguration:
public class DynamicRegexValidator implements ConstraintValidator<RegexValidatedField, String> { private ValidatorConfiguration validatorConfiguration; private String fieldName; @Override public void initialize(RegexValidatedField constraintAnnotation) { this.fieldName = constraintAnnotation.fieldName(); // 从Spring上下文获取Bean this.validatorConfiguration = SpringContextHolder.getBean(ValidatorConfiguration.class); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (validatorConfiguration == null) { return true; // 根据业务需求调整默认逻辑 } ValidationRule rule = validatorConfiguration.getRuleForField(fieldName); if (rule == null || value == null) { return true; } return value.matches(rule.getRegex()); } }
方案二:自定义ConstraintValidatorFactory让Spring管理校验器
创建Spring托管的校验器工厂,让Hibernate使用该工厂创建校验器实例:
@Component public class SpringConstraintValidatorFactory implements ConstraintValidatorFactory { private final AutowireCapableBeanFactory beanFactory; @Autowired public SpringConstraintValidatorFactory(AutowireCapableBeanFactory beanFactory) { this.beanFactory = beanFactory; } @Override public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) { // 由Spring创建校验器实例,支持Autowired注入 return beanFactory.createBean(key); } @Override public void releaseInstance(ConstraintValidator<?, ?> instance) { beanFactory.destroyBean(instance); } }
配置Hibernate Validator使用自定义工厂:
@Configuration public class ValidatorConfig { @Autowired private SpringConstraintValidatorFactory validatorFactory; @Bean public Validator validator() { return Validation.byProvider(HibernateValidator.class) .configure() .constraintValidatorFactory(validatorFactory) .buildValidatorFactory() .getValidator(); } }
此方案无需修改原DynamicRegexValidator代码,@Autowired可正常注入Spring Bean。
注意事项
- 方案一中需确保
SpringContextHolder被Spring扫描到(处于@ComponentScan范围内) - 方案二中自定义的
ValidatorBean会替换Spring默认的Validator实例,确保所有校验逻辑都使用该工厂创建的校验器
内容的提问来源于stack exchange,提问作者jun
相关产品推荐
相关产品推荐

