java - 使用 RESTeasy 3 validator 进行 Spring 4 依赖注入(inject)

标签 java spring resteasy bean-validation

我在尝试获取自定义 Bean 验证约束以成功利用 Spring 的依赖注入(inject)时遇到了很多麻烦。

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

@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 实现

@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 字段始终为 null。

版本
Spring :4.0.4.发布
RESTeasy:3.0.6.最终版

最佳答案

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

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

查看 org.jboss.resteasy:resteasy-validator-provider-11:3.0.6.Final 我发现它主要是设置一个 @Provider 实例,该实例提供 Resteasy 用来解析 Bean Validation 的 GeneralValidator。我注意到的问题是提供者类 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 依赖注入(inject)在我的自定义验证约束实现中工作,就像问题中一样。

理想的前进道路是拥有一个与 Spring 配合使用的备用 Resteasy-validator-provider-11。

关于java - 使用 RESTeasy 3 validator 进行 Spring 4 依赖注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23940911/

相关文章:

java - 通用分词器

java - 获取数字流的范围

java - 需要打印不带元音的扫描仪字符串的代码

java - 在自定义 Unity Android 插件上调用非静态方法

java - IntelliJ 中的 Spring MVC 4 项目模板

spring - spring请求参数转换怎么做

java - Spring MVC - 将枚举填充到下拉列表

java - 直接向 Quarkus/RESTEasy Web 服务方法提供 SecurityIdentity

serialization - 我可以在 RestEasy 中指定用于方法结果转换的 jackson @JsonView 吗?

java - key 斗篷错误 : Unrecognized field "access_token"