使用RESTeasy 3验证程序获取Spring 4依赖项注入 [英] Getting Spring 4 dependency injection working with RESTeasy 3 validator

查看:84
本文介绍了使用RESTeasy 3验证程序获取Spring 4依赖项注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

尝试获取自定义Bean验证约束以成功利用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-validator-provider-11:3.0.6.Final

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

版本
春季:4.0.4.Release
RESTeasy:3.0.6.Final

解决方案

我已经找到了一种解决方案,虽然它不是理想的解决方案,但是它确实可以工作,并且有可能演变成合适的解决方案.

在阅读了许多StackOverflow问题和各种Web文章之后,我几乎没有发现任何有关使Spring和Resteasy进行验证的引用.有关Spring和Bean验证的参考文献很多,但是一旦引入Resteasy,讨论就消失了.从其他讨论中可以明显看出,Spring提供了一个称为LocalValidatorFactoryBean的ValidatorFactory实现,该实现将在验证器实现中启用Spring依赖项注入.

查看org.jboss.resteasy:resteasy-validator-provider-11:3.0.6.最后,我发现它主要是建立一个@Provider实例,该实例提供Resteasy用来解析Bean验证的GeneralValidator.我注意到的问题是提供程序类

SpringValidationConfig
在Spring中设置LocalValidatorFactoryBean bean.您可以根据需要执行此操作,我更喜欢以编程方式

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

最后配置Spring以加载这两个类.我通过在Spring配置中执行component-scan来做到这一点,

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

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

前进的理想之路是拥有一个与Spring一起使用的替代resteasy-validator-provider-11.

I have been running into a lot of trouble trying to get custom Bean Validation constraints to successfully leverage Spring's dependency injection.

For example I might define a constraint:

@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 {};
}

Validator Implementation

@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;
    }

}

The standard way to setup Bean Validation with RESTeasy is to pull in: org.jboss.resteasy:resteasy-validator-provider-11:3.0.6.Final

However when the validation is executed at runtime, any @Autowired field is always null.

Versions
Spring: 4.0.4.Release
RESTeasy: 3.0.6.Final

解决方案

I have found a solution, it is not ideal yet but it does work and could evolve into a proper solution.

After reading through many StackOverflow questions and various web articles I found hardly any references to getting Spring and Resteasy working with validation. There are plenty of references for Spring and Bean Validation, but once Resteasy is introduced the discussion disappears. What is clear from other discussions is that Spring provides a ValidatorFactory implementation called LocalValidatorFactoryBean that will enabled Spring dependency injection within validator implementations.

Looking into org.jboss.resteasy:resteasy-validator-provider-11:3.0.6.Final I found out it mostly is setting up a @Provider instance that provides the GeneralValidator used by Resteasy to resolve Bean Validation. The problem I noticed was that the provider class, ValidatorContextResolver, does not really provide any way to set up a different version of the ValidatorFactory. It tries to load via CDI / JNDI and if that fails then defaults to a hardcoded ValidatorFactory implementation. In my instance I can't rely on the JNDI lookup so there was no way to provide Spring's LocalValidatorFactoryBean as the factory.

On the other hand the resteasy-validator-provider-11 was a very small library and the code that would need to change to instead use Spring's LocalValidatorFactoryBean was minimal. You can drop the resteasy-validator-provider-11 dependency and reimplement the library to use Spring instead. I will show related code below that will get this working, leaving out classes from the resteasy-validator-provider-11 library that I did not change.

ValidatorContextResolver
You need to get a copy of the LocalValidatorFactoryBean bootstrapped by Spring, you cannot just instantiate it with new here:

@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
Setup the LocalValidatorFactoryBean bean in Spring. You can do this however you want, I prefer programmatically

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

And finally configure Spring to load the both of those classes. I do it by component-scan in my Spring config like:

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

Following these steps I was able to get the @Autowired dependency injection working in my custom validation constraint implementations like in the question.

The ideal path forward would be to have an alternate resteasy-validator-provider-11 that works with Spring.

这篇关于使用RESTeasy 3验证程序获取Spring 4依赖项注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆