【问题标题】:Injecting Spring Dependencies into ConstrantValidator将 Spring 依赖项注入 ConstrantValidator
【发布时间】:2019-09-24 21:10:44
【问题描述】:

我正在使用 Bean 验证。我有一个自定义验证器@MyValidator,它需要使用注入的 Spring 托管 DAO 对象查找值。我怎样才能访问这个? Spring 没有将 DAO 注入到我的“MyValidator”对象中。

@Component
public class CodeListValidator implements ConstraintValidator<CodeList, String> {
    @Autowired
    private ICodeListCommonService codeListCommonService;

    private CodeListEnum codeListID;

    @Override
    public void initialize(CodeList constraintAnnotation) {
        this.codeListID = constraintAnnotation.value();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return codeListCommonService.doesCodeEntityExistForCodeList(codeListID.getDbCodeListId(), value, ProviderConstants.CODE_LIST_STATUS_ACTIVE);
    }
}

“codeListCommonService”为空。这是因为 Spring 没有创建该类 - 但我怎样才能让它与 Spring AoP 一起使用?

这个验证器的使用是这样的:


        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();

        MyObject validateMe = new MyObject();

        Set<ConstraintViolation<MyObject>> constraintViolations = validator.validate(validateMe);

对于我的对象:

public class MyObject {
   @Size(max = 1)
   @CodeList(CodeListEnum.CARTYPE)
   public String carType;
}

因此,当验证器运行时,它会处理注释...我只需要将服务注入到我对其所做的 CodeListValidator 中,就可以进行数据库查找,以根据“有效汽车类型值”的数据库列表验证该值”。

编辑:解决方案:

玩弄了创建 Spring 感知工厂的想法 - 与现有代码的过多集成。

似乎最好的解决方案(并且在这里有效)是创建一个 Spring 服务,将 ApplicationContext 存储在静态方法中,以便“非 Spring 托管”bean 可以访问它们。

所以一个新的服务:

@Service
public class SpringApplicationContextService implements ISpringApplicationContextService, ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
}

然后任何验证器或非 Spring bean 都可以通过以下方式获取 Spring bean:

public class CodeListValidator implements ConstraintValidator<CodeList, String> {
    @Autowired
    private ICodeListCommonService codeListCommonService;

    private CodeListEnum codeListID;

    @Override
    public void initialize(CodeList constraintAnnotation) {
        this.codeListID = constraintAnnotation.value();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        ICodeListCommonService codeListCommonService = SpringApplicationContextService.getApplicationContext().getBean(ICodeListCommonService.class);
        return codeListCommonService.doesCodeEntityExistForCodeList(codeListID.getDbCodeListId(), value, ProviderConstants.CODE_LIST_STATUS_ACTIVE);
    }
}

当然,这一切的原因(在这个应用程序中使用了几十次):

    @CodeList(CodeListEnum.EMAIL_OPT_OUT_FLAG)
    public String emailOptOutFlag;

    @CodeList(CodeListEnum.CLEARING_HOUSE)
    public String clearingHouse;

【问题讨论】:

  • 您可能需要发布更多代码,MyValidator 未知,您要在哪里自动连接 MyValidator?
  • 我正在制作自己的验证器——我还有其他验证器在工作,但这个需要注入的 Spring 服务。我看到了问题 - 验证器来自工厂,它似乎必须“感知 Spring”......添加更多代码

标签: spring bean-validation


【解决方案1】:

@Autowired 在 ConstraintValidator 实现中正常工作的最低设置是在 Spring @Configuration 中拥有这个 bean:

    @Bean
    public Validator defaultValidator() {
        return new LocalValidatorFactoryBean();
    }

这允许将包括ApplicationContext 在内的任何bean 直接注入ConstraintValidator:

@Constraint(validatedBy = DemoValidator.class)
public @interface DemoAnnotation {
    // ...
    Class<?> beanClass();
}

public class DemoValidator implements ConstraintValidator<DemoAnnotation, String> {

    private final ApplicationContext applicationContext;

    private Object bean;

    @Autowired
    public DemoValidator(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void initialize(DemoAnnotation constraint) {
        Class<?> beanClass = constraint.beanClass();
        bean = applicationContext.getBean(beanClass);
    }

    @Override
    public boolean isValid(String obj, ConstraintValidatorContext context) {
        return !obj.isEmpty();
    }
}

Demo

对于真正灵活的验证解决方案,我推荐 Jakub Jirutka's Bean Validator utilizing Spring Expression Language (SpEL),它允许:

public class Sample {

    @SpELAssert("@myService.calculate(#this) > 42")
    private int value;
}

【讨论】:

    猜你喜欢
    • 2011-06-12
    • 1970-01-01
    • 2012-08-22
    • 1970-01-01
    • 1970-01-01
    • 2011-01-26
    • 2012-07-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多