我在尝试获取自定义 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/