Spring自定义校验注解因Autowired仓库空指针失效,求解决方案
解决Spring自定义校验器中依赖注入为null的问题
问题根源:Hibernate Validator默认会自行实例化自定义校验器(比如你的NameValidator),而非由Spring容器管理实例。所以即使给校验器添加@Component或@Service注解,Spring也无法对其内部的@Autowired成员完成注入,导致nicknameRepository为空。
下面是几种可行的解决办法:
方法1:配置Spring管理的Validator工厂
通过自定义LocalValidatorFactoryBean,指定使用SpringConstraintValidatorFactory,让所有校验器都由Spring容器创建和管理,这样就能正常进行依赖注入。
添加如下配置类:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.validation.beanvalidation.SpringConstraintValidatorFactory; @Configuration public class ValidatorConfig { @Bean public LocalValidatorFactoryBean validator(org.springframework.context.ApplicationContext applicationContext) { LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean(); factoryBean.setConstraintValidatorFactory(new SpringConstraintValidatorFactory(applicationContext.getAutowireCapableBeanFactory())); return factoryBean; } }
同时给NameValidator添加@Component注解,确保Spring能扫描到它:
@Component public class NameValidator implements ConstraintValidator<NameValidation, String> { @Autowired private NicknameRepository nicknameRepository; @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { return nicknameRepository.existsById(1L); } }
方法2:手动从Spring上下文获取Bean
如果不想修改全局Validator配置,可以在校验器中通过ApplicationContext手动获取仓库实例。
首先创建一个Spring上下文工具类:
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext context) { applicationContext = context; } public static <T> T getBean(Class<T> beanClass) { return applicationContext.getBean(beanClass); } }
然后修改NameValidator:
public class NameValidator implements ConstraintValidator<NameValidation, String> { private NicknameRepository nicknameRepository; @Override public void initialize(NameValidation constraintAnnotation) { // 初始化时从上下文获取Bean this.nicknameRepository = SpringContextUtil.getBean(NicknameRepository.class); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { return nicknameRepository.existsById(1L); } }
方法3:业务层校验替代实体注解校验
如果你的校验逻辑需要依赖其他服务/仓库,也可以把校验逻辑移到Service层,不在实体类上使用自定义注解。比如在保存Nickname的Service方法中先进行校验:
@Service public class NicknameService { @Autowired private NicknameRepository nicknameRepository; public Nickname saveNickname(Nickname nickname) { // 执行自定义校验逻辑 if (!nicknameRepository.existsById(1L)) { throw new IllegalArgumentException("Name is invalid"); } return nicknameRepository.save(nickname); } }
内容的提问来源于stack exchange,提问作者MSmith
相关产品推荐
相关产品推荐

