Autowired在Custom Constraint验证器中提供Null值

时间:2016-06-22 03:40:08

标签: java spring validation spring-mvc hibernate-validator

我对Spring完全不熟悉,因为问题问题,我已经在SO上找了几个答案。以下是链接:

Spring 3.1 Autowiring does not work inside custom constraint validator

Autowiring a service into a validator

Autowired Repository is Null in Custom Constraint Validator

我有一个Spring项目,我想在其中使用Hibernate Validator进行对象验证。根据我在网上阅读的内容和一些论坛,我试图按如下方式注入验证器:

$HOME/.atom

但无论我在哪里使用

@Bean
  public Validator validator() {
    return new LocalValidatorFactoryBean().getValidator();
  }

它采用了Spring的验证器实现而不是Hibernate的验证器。我无法弄清楚如何准确地注入Hibernate验证器并简单地将其自动装配到其他类中,所以我使用了一个便宜的技巧,现在我的Java Config看起来像这样

@Autowired
Validator validator;

我真的很感激,如果有人能够指出我如何避免以这种Hacky方式获取Hibernate Validator 的正确方向)

但是让我们来看看主要问题:

这是自定义验证定义

@Bean
      public Validator validator() {
        // ValidatorImpl is Hibernate's implementation of the Validator
        return new ValidatorImpl();
      }

我的自定义验证器

@Target( { METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER } )
@Retention(RUNTIME)
@Constraint(validatedBy = EmployeeValidator.class)
@Documented
public @interface EmployeeValidation {
String message() default "{constraints.employeeConstraints}";
public abstract Class<?>[] groups() default {};
public abstract Class<? extends Payload>[] payload() default {};
}

在上面的Custom Constraint Validator中,我得到employeeService null。我知道Spring启动时没有实例化ConstraintValidator的任何实现,但我认为添加ValidatorImpl()实际上会解决这个问题。但它没有。

现在我遇到了一个非常糟糕的解决方法,我不想继续使用这样的代码。有人可以帮助我解决我的情况。

P.S。这些是我在Java Config文件中的导入:

public class EmployeeValidator implements ConstraintValidator<EmployeeValidation , Object> {

@Autowired
private EmployeeService employeeService;

@Override
public void initialize(EmployeeValidation constraintAnnotation) {
//do Something
 }

@Override
public boolean isValid(String type, ConstraintValidatorContext context) {
    return false;
}
}

7 个答案:

答案 0 :(得分:12)

我希望解决方案可以帮助某人:

@Bean
public Validator validator () {

    ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
        .configure().constraintValidatorFactory(new SpringConstraintValidatorFactory(autowireCapableBeanFactory))
        .buildValidatorFactory();
    Validator validator = validatorFactory.getValidator();

    return validator;
}

使用SpringConstraintValidatorFactory初始化验证器以便注入工作并将验证器实现提供为Hibernate.class以下列方式工作:

  1. 您的对象将由您选择的库验证
  2. 您的自定义验证器将能够使用Spring的功能,同时让Hibernate执行验证。
  3. 工作原理: Hibernate的ConstraintValidatorFactory不会初始化任何ConstraintValidators,除非它们被调用,但SpringConstraintValidatorFactory通过向AutowireCapableBeanFactory提供{。}}。

    修改

    如@shabyasaschi的评论之一所述,要注入autowireCapableBeanFactory,您可以将方法签名更改为:

    Validator validator(final AutowireCapableBeanFactory autowireCapableBeanFactory) {
    

    或在配置文件中为它添加getter和setter,如下所示:

    public AutowireCapableBeanFactory getAutowireCapableBeanFactory() {
            return autowireCapableBeanFactory;
    }
    
    public void setAutowireCapableBeanFactory(AutowireCapableBeanFactory autowireCapableBeanFactory) {
            this.autowireCapableBeanFactory = autowireCapableBeanFactory;
    }
    

答案 1 :(得分:0)

在你的bean定义中

img {
    width: 500px;
    height: 200px;
    position: absolute;
}
.projectnaam {
    text-align:left;
    font: 30px/30px 'grotesque_mtextracondensed', 'Arial Black', 'Arial Bold', sans-serif;
    text-transform: uppercase;
    padding:0 0 0 20px;
    background-color:red;
    margin-top: 100px;
}

方法定义中@Bean public Validator validator() { return new LocalValidatorFactoryBean().getValidator(); } 的类型是什么?你应该确保它从Spring返回Validator而不是 javax.validation.Validator

让Spring引导验证器也会导致Validator传递给Hibernate Validator,它将在约束验证器中启用依赖注入。

答案 2 :(得分:0)

你可以用两个方法解决这个问题:

  • 尝试使用Spring在您的验证器上注入服务。

  • 手动初始化,覆盖Validator的初始化方法。

我之前遇到同样的问题,最后我决定使用第二种选择来避免大量问题。

正如您所指出的,您必须在验证器上定义一个初始化方法,然后您可以使用ServiceUtils来获取所需的服务bean:

@Autowired
private EmployeeService employeeService;

@Override
public void initialize(EmployeeValidation constraintAnnotation) {
  //Use an utility service to get Spring beans
  employeeService = ServiceUtils.getEmployeeService();
}

ServiceUtils是一个普通的Spring bean,它在静态方法中使用了自己的静态引用。

@Component
public class ServiceUtils {
  private static ServiceUtils instance;

  @Autowired
  private EmployeeService employeeService;

  /* Post constructor */

  @PostConstruct
  public void fillInstance() {
    instance = this;
  }

  /*static methods */

  public static EmployeeService getEmployeeService) {
    return instance.employeeService;
  }
}

因此,您使用Spring注入所需的服务,但不是通常的方式。 希望这会有所帮助。

答案 3 :(得分:0)

那对我有用。对于目前正在搜索的人

public class EmployeeValidator implements ConstraintValidator<EmployeeValidation , Object> {


    private EmployeeService employeeService;

    public EmployeeValidator(EmployeeService employeeService){
        this.employeeService = employeeService;
    }

    ...
}

答案 4 :(得分:0)

您的代码没有任何问题 这取决于您如何创建 ValidatorFactory。 创建一个 bean 并让 Spring 处理它。

@Bean
public ValidatorFactory validatorFactory(){
    return Validation.buildDefaultValidatorFactory();
}

答案 5 :(得分:0)

想一想。在 Constraint 验证器类中使用 @Autowired 应该没有问题。这意味着有问题。

这个问题已经在各个平台上报告了,我还没有看到好的解决方案。不过,我已经看到了一些解决方法。

这是我发现的。 您可能会注意到验证发生了两次。第一次它应该可以工作,但第二次您收到与空相关的错误消息。问题应该是您正在验证的实体或类在您的控制器中被使用了两次。例如,您可能希望验证实体类并尝试在控制器方法中的同一方法中同时保存它。当您尝试保存实体时,它会再次尝试验证对象,这次@Autowired 对象将为空。

您可以针对这种情况执行以下操作

您可以使用dto来携带验证注解并将dto类的属性复制到您的实体类中,然后再将其保存到数据库中。您的情况可能有所不同,但解决方法应该是相同的。

下面是运行代码的说明

public ResponseEntity<InstitutionModel> create(@Valid @RequestBody InstitutionDto institutiondto) {
    Institution institution = new Institution();
    BeanUtils.copyProperties(institutiondto, institution);
    return Optional.of(this.institutionService.save(institution)).map(institutionModelAssembler::toModel)
            .map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
}

答案 6 :(得分:-2)

private XXXService xxxService = SpringContextHolder.getBean(XXXService.class);