简体   繁体   English

Autowired 在自定义约束验证器中给出 Null 值

[英]Autowired gives Null value in Custom Constraint validator

I am totally new to Spring and I have looked in to a few answers on SO for the asked problem.我对 Spring 完全陌生,我已经查看了一些关于 SO 的问题的答案。 Here are the links:以下是链接:

Spring 3.1 Autowiring does not work inside custom constraint validator Spring 3.1 自动装配在自定义约束验证器中不起作用

Autowiring a service into a validator 将服务自动装配到验证器中

Autowired Repository is Null in Custom Constraint Validator 自定义约束验证器中的自动装配存储库为空

I have a Spring project in which I want to use Hibernate Validator for an object validation.我有一个 Spring 项目,我想在其中使用 Hibernate Validator 进行对象验证。 Based on what I read online and a few forums I tried to inject validator as follows:根据我在网上阅读的内容和一些论坛,我尝试按如下方式注入验证器:

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

But wherever I was using但无论我在哪里使用

@Autowired
Validator validator;

It was taking Spring's validator implementation instead of the Hibernate's validator.它采用 Spring 的验证器实现而不是 Hibernate 的验证器。 I couldn't figure out how to exactly inject Hibernate validator and simply Autowire it across other classes so I used a cheap trick, now my Java Config looks like this我不知道如何准确地注入 Hibernate 验证器并简单地将它自动连接到其他类,所以我使用了一个便宜的技巧,现在我的 Java 配置看起来像这样

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

( I would really appreciate if someone can actually point me into the right direction on how to avoid getting Hibernate Validator in this Hacky way ) 如果有人能真正为我指明如何避免以这种 Hacky 方式获取 Hibernate Validator 的正确方向,我将不胜感激

But lets come to the main issue here:但是让我们来看看这里的主要问题:

Here is custom validation definition这是自定义验证定义

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

My Custom Validator我的自定义验证器

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

In the above Custom Constraint Validator I get the employeeService null.在上面的自定义约束验证器中,我得到了 employeeService 为空。 I know that any implementations of ConstraintValidator are not instantiated when Spring is starting up but I thought adding the ValidatorImpl() will actually fix that.我知道在 Spring 启动时没有实例化 ConstraintValidator 的任何实现,但我认为添加 ValidatorImpl() 实际上会解决这个问题。 But it didn't.但它没有。

Now I am stuck with a really hacky workaround and I do not want to continue with a code like that.现在我陷入了一个非常棘手的解决方法,我不想继续使用这样的代码。 Can someone please help me with my situation.有人可以帮助我解决我的情况。

PS These are my imports in the Java Config file: PS 这些是我在 Java 配置文件中的导入:

import org.hibernate.validator.HibernateValidator; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.MessageSource; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.context.annotation.PropertySource; 
import org.springframework.context.support.ReloadableResourceBundleMessageSource; 
import org.springframework.core.env.Environment; 
import org.springframework.validation.Validator; 
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; 
import org.springframework.web.servlet.LocaleResolver; 
import org.springframework.web.servlet.ViewResolver; 
import org.springframework.web.servlet.config.annotation.EnableWebMvc; 
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 
import org.springframework.web.servlet.i18n.CookieLocaleResolver; 
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; 
import org.springframework.web.servlet.view.InternalResourceViewResolver; 

I hope the solution will help someone:我希望该解决方案能帮助某人:

@Bean
public Validator validator () {

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

    return validator;
}

Initializing the validator with SpringConstraintValidatorFactory so that injection works and providing the validator implementation to be Hibernate.class works in the following manner:使用SpringConstraintValidatorFactory初始化验证器以便注入工作并提供验证器实现为 Hibernate.class 以以下方式工作:

  1. Your objects will be validated by the library of your choice您的对象将由您选择的库进行验证
  2. Your custom validators will be able to use Spring's functionality while having validation to be executed by Hibernate.您的自定义验证器将能够使用 Spring 的功能,同时让 Hibernate 执行验证。

How it works: Hibernate's ConstraintValidatorFactory does not initialize any ConstraintValidators unless they are called but SpringConstraintValidatorFactory does by giving AutowireCapableBeanFactory to it.工作原理:Hibernate 的ConstraintValidatorFactory不会初始化任何ConstraintValidators除非它们被调用,但SpringConstraintValidatorFactory是通过给它AutowireCapableBeanFactory来实现的。

EDIT编辑

As mentioned in one of the comments by @shabyasaschi To inject autowireCapableBeanFactory you can change the method signature as:正如@shabyasaschi 的评论之一所述,要注入autowireCapableBeanFactory您可以将方法签名更改为:

Validator validator(final AutowireCapableBeanFactory autowireCapableBeanFactory) {

or add getter and setter for it in the config file as follows:或者在配置文件中为它添加 getter 和 setter,如下所示:

public AutowireCapableBeanFactory getAutowireCapableBeanFactory() {
        return autowireCapableBeanFactory;
}

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

You can fix this with two aproaches:您可以使用两种方法解决此问题:

  • Try to inject Services on your validator using Spring.尝试使用 Spring 在您的验证器上注入服务。

  • Initialize it manually overriding Validator's initialize method.手动初始化它,覆盖 Validator 的 initialize 方法。

I had the same problem time ago and finally i decided to use second option avoiding tons of problems.前一段时间我遇到了同样的问题,最后我决定使用第二个选项来避免大量问题。

As you point you must define one initialize method on your validator and there you can use a ServiceUtils to get the service bean you need:当您指出时,您必须在验证器上定义一个 initialize 方法,然后您可以使用 ServiceUtils 来获取您需要的服务 bean:

@Autowired
private EmployeeService employeeService;

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

And ServiceUtils is a normal Spring bean with a static reference to itself used in the static methods. 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;
  }
}

So you are using Spring to inject the services you need but not in the usual way.所以您正在使用 Spring 注入您需要的服务,但不是以通常的方式。 Hope this helps.希望这会有所帮助。

In your bean definition在你的 bean 定义中

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

What's the type of Validator in the method definition?方法定义中的Validator类型是什么? You should make sure it returns javax.validation.Validator , not Validator from Spring.您应该确保它返回javax.validation.Validator而不是Spring 的Validator

Letting Spring bootstrap the validator will it also cause to pass a SpringConstraintValidatorFactory to Hibernate Validator which will enable dependency injection within constraint validators.让 Spring 引导验证器还会导致将SpringConstraintValidatorFactory传递给 Hibernate Validator,这将在约束验证器中启用依赖项注入。

There is nothing wrong with your code It depends how are you creating your ValidatorFactory.您的代码没有任何问题这取决于您如何创建 ValidatorFactory。 Create a bean and let Spring handle it.创建一个 bean 并让 Spring 处理它。

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

Think about it.想想看。 There should have been no issue with using the @Autowired inside a Constraint validator class.在 Constraint 验证器类中使用 @Autowired 应该没有问题。 This means that something is wrong.这意味着有些事情是错误的。

This issue has been reported on various platform and I have not seen a good solution.此问题已在各种平台上报告,我还没有看到好的解决方案。 I have seen some workaround though.不过,我已经看到了一些解决方法。

Here is what I found.这是我发现的。 You may notice that the validation is happening twice.您可能会注意到验证发生了两次。 the first time it should work but the second time you got a null related error message.第一次它应该可以工作,但第二次您收到与空相关的错误消息。 The problem should be that the entity or the class that you is being validated is being used twice in your controller.问题应该是您正在验证的实体或类在您的控制器中被使用了两次。 For example, you may want validate the entity class and try to save it at the same time in the same method in the controller method.例如,您可能希望验证实体类并尝试在控制器方法中的同一方法中同时保存它。 when you try to save the entity, it will try to validate the object again and this time the @Autowired object will be null.当您尝试保存实体时,它会再次尝试验证对象,这次 @Autowired 对象将为空。

Here is what you can do for this scenario以下是您可以针对此场景执行的操作

You can use dto to carry the validation annotation and copy the property of the dto class to your entity class before you save it into the database.您可以使用 dto 携带验证注解并将 dto 类的属性复制到您的实体类中,然后再将其保存到数据库中。 your scenario may be different but the solution approach should be the same.您的情况可能有所不同,但解决方法应该是相同的。

Below is an illustration of code that works下面是一个有效的代码说明

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());
}
private XXXService xxxService = SpringContextHolder.getBean(XXXService.class);

Thats worked for me.那对我有用。 For guys who search at now对于现在搜索的人

public class EmployeeValidator implements ConstraintValidator<EmployeeValidation , Object> {


    private EmployeeService employeeService;

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

    ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM