让Spring 4依赖注入与RESTeasy 3验证器一起使用

时间:2014-05-29 18:57:46

标签: java spring resteasy bean-validation

尝试获取自定义Bean Validation约束以成功利用Spring的依赖注入时,我遇到了很多麻烦。

例如,我可能会定义一个约束:

@Constraint(validatedBy = { CustomConstraintValidator.class })
@Documented
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomConstraint {
    String message() default "custom.constraint.error";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

验证码实施

@Component
public class CustomConstraint implements ConstraintValidator<CustomConstraint, String> {

    @Autowired
    private SomeSpringBean someSpringBean;

    @Override
    public void initialize(CustomConstraint constraintAnnotation) {
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {

        if (value != null) {
            SomeObject storedValue = someSpringBean.find(value);

                    return storedValue != null;
        }

        return true;
    }

}

使用RESTeasy设置Bean验证的标准方法是: org.jboss.resteasy:RestEasy的验证器提供商-11:3.0.6.Final

但是,在运行时执行验证时,任何@Autowired字段始终为空。

版本
Spring:4.0.4。发布 RESTeasy:3.0.6.Final

1 个答案:

答案 0 :(得分:1)

我找到了一个解决方案,但它并不理想,但确实有效,可以演变成一个合适的解决方案。

在阅读了许多StackOverflow问题和各种网络文章后,我发现几乎没有任何关于让Spring和Resteasy使用验证的参考。 Spring和Bean验证有很多参考,但一旦引入Resteasy,讨论就会消失。从其他讨论中可以清楚地看出,Spring提供了一个名为LocalValidatorFactoryBean的ValidatorFactory实现,它将在验证器实现中启用Spring依赖注入。

调查org.jboss.resteasy:resteasy-validator-provider-11:3.0.6.Final我发现它主要是设置一个@Provider实例,它提供Resteasy使用的GeneralValidator来解析Bean Validation。我注意到的问题是提供程序类ValidatorContextResolver并没有真正提供任何方法来设置ValidatorFactory的不同版本。它尝试通过CDI / JNDI加载,如果失败则默认为硬编码的ValidatorFactory实现。在我的实例中,我不能依赖JNDI查找,因此无法提供Spring的LocalValidatorFactoryBean作为工厂。

另一方面,resteasy-validator-provider-11是一个非常小的库,而需要改为使用Spring的LocalValidatorFactoryBean的代码是最小的。您可以删除resteasy-validator-provider-11依赖项并重新实现库以使用Spring。我将在下面显示相关代码,这将使这个工作,从我没有改变的resteasy-validator-provider-11库中省略了类。

<强> ValidatorContextResolver
你需要获取Spring引导的LocalValidatorFactoryBean的副本,你不能在new实例化它:

@Component
@Provider
public class ValidatorContextResolver implements ContextResolver<GeneralValidator> {
    private final static Logger log = Logger.getLogger(ValidatorContextResolver.class);
    private volatile ValidatorFactory validatorFactory;
    final static Object RD_LOCK = new Object();

    @Autowired
    private ValidatorFactory springValidatorFactoryBean;

    // this used to be initialized in a static block, but I was having trouble class loading the context resolver in some
    // environments. So instead of failing and logging a warning when the resolver is instantiated at deploy time
    // we log any validation warning when trying to obtain the ValidatorFactory.
    ValidatorFactory getValidatorFactory() {
        ValidatorFactory tmpValidatorFactory = validatorFactory;
        if (tmpValidatorFactory == null) {
            synchronized (RD_LOCK) {
                tmpValidatorFactory = validatorFactory;
                if (tmpValidatorFactory == null) {
                    log.info("Obtaining Spring-bean enabled validator factory");
                    validatorFactory = tmpValidatorFactory = springValidatorFactoryBean;
                }
            }
        }
        return validatorFactory;
    }

    @Override
    public GeneralValidator getContext(Class<?> type) {
        try {
            Configuration<?> config = Validation.byDefaultProvider().configure();
            BootstrapConfiguration bootstrapConfiguration = config.getBootstrapConfiguration();
            boolean isExecutableValidationEnabled = bootstrapConfiguration.isExecutableValidationEnabled();
            Set<ExecutableType> defaultValidatedExecutableTypes = bootstrapConfiguration.getDefaultValidatedExecutableTypes();
            return new GeneralValidatorImpl(getValidatorFactory(), isExecutableValidationEnabled, defaultValidatedExecutableTypes);
        } catch (Exception e) {
            throw new ValidationException("Unable to load Validation support", e);
        }
    }
}

<强> SpringValidationConfig
在Spring中设置LocalValidatorFactoryBean bean。您可以随意执行此操作,我更喜欢以编程方式

@Configuration
public class DatabaseConfig
{
    @Bean
    public ValidatorFactory validator() {
        LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
       // localValidatorFactoryBean.setValidationMessageSource(getValidationMessageSource());
        return localValidatorFactoryBean;
    }
}

最后配置Spring来加载这两个类。我在Spring配置中通过组件扫描来完成它,如:

<context:component-scan base-package="org.example.resteasy.spring.validation.config" />

按照这些步骤,我能够在我的自定义验证约束实现中使用@Autowired依赖注入工作。

理想的前进方向是拥有一个适用于Spring的备用resteasy-validator-provider-11。

相关问题