Dependency injection using Guice within Constraint validator

时光总嘲笑我的痴心妄想 提交于 2019-12-11 00:16:25

问题


I have a use case of injecting a class inside an implementation of ConstraintValidator. I'm using Google guice for Dependency injection and am currently unable to inject inside the validator.

Simplified form of my scenario

Inside Module:

@Provides
@Singleton
public ServiceA getServiceA() {
        return new ServiceA();
}

The constraint validator:

public class MyValidator implements ConstraintValidator<ValidS,List<String>> {

    private final ServiceA serviceA;

    @Inject
    public MyValidator(ServiceA serviceA) {
         this.serviceA = serviceA;
    }
    @Override
    public void initialize(final ValidS validS) {
    }

    @Override
    public boolean isValid(final List<String> sList, final ConstraintValidatorContext constraintValidatorContext) {
        System.out.println(serviceA.testInjection());
        //validation code
    }


}

Edit: Adding the exception message:

HV000064: Unable to instantiate ConstraintValidator: class com.validation.MyValidator.
javax.validation.ValidationException: HV000064: Unable to instantiate ConstraintValidator: class com.validation.MyValidator.
    at org.hibernate.validator.internal.util.privilegedactions.NewInstance.run(NewInstance.java:51) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.util.ReflectionHelper.run(ReflectionHelper.java:671) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.util.ReflectionHelper.newInstance(ReflectionHelper.java:219) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl.getInstance(ConstraintValidatorFactoryImpl.java:34) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager.createAndInitializeValidator(ConstraintValidatorManager.java:141) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager.getInitializedValidator(ConstraintValidatorManager.java:101) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:125) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:91) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:85) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:478) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:424) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:388) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:340) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:158) ~[hibernate-validator-5.0.1.Final.jar:5.0.1.Final]
    at com.MyClass.validate(MyClass.java:48) 

回答1:


Your custom validator cannot be instantiated by Hibernate, as Hibernate expects the validator to have a no arg constructor (hibernate docs):

The default ConstraintValidatorFactory provided by Hibernate Validator requires a public no-arg constructor to instantiate ConstraintValidator instances (see The constraint validator).

So you basically have two possibilities:

  1. Implement a custom ConstraintValidatorFactory, this is described in the linked documentation, and in that factory, get access to the guice injector to get the ServiceA and pass it to the constructor.

  2. Remove the public MyValidator(ServiceA serviceA) constructor and try to initialize the reference to ServiceA from within the public void initialize(final ValidS validS)method. Here you need to get the guice injector as well.

To get the injector you could do the following (this is no nice code, but somehow you need to get access to the injector). To store your injector reference you could use a Singleton - here I implement it by using an enum:

public enum GlobalInjector {
  INSTANCE;
  private Injector injector;
  public Injector getInjector() {
    return injector;
  }
  public void setInjector(Injector injector) {
    this.injector = injector;
  }      
}

I do not know how you initialize guice, but you probably have some code like this, so add the code to store the injector:

Injector injector = Guice.createInjector(new ConfigurationModule());
GlobalInjector.INSTANCE.setInjector(injector);

Then your initialize method would look like:

@Override
public void initialize(final ValidS validS) {
  Injector injector = GlobalInjector.INSTANCE.getInjector();
  if(null != injector) {
    serviceA = injector.getInstance(ServiceA.class);
  }
}

Do not forget to check if GlobalInjector.INSTANCE.getInjector(); returns not null, and when your validate method is called, make sure that serviceA is not null.

Note: personally I do not like the idea of storing the injector as a global variable (disguised by a singleton), but at the moment, I have no better idea. This code is not tested, so I hope it all works as intended.



来源:https://stackoverflow.com/questions/43485410/dependency-injection-using-guice-within-constraint-validator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!