尝试获取自定义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
答案 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。