Подтвердить что ты не робот

Guice и интерфейс, который имеет множество реализаций

Если у меня есть интерфейс Validator и несколько реализаций для этого интерфейса. Как я могу ввести любую из нескольких реализаций с помощью Guice? Теперь я знаю, что я могу использовать следующий код для ввода одного, но он допускает только одну реализацию:

public class MyModule extends AbstractModule {
  @Override 
  protected void configure() {
    bind(Validator.class).to(OneOfMyValidators.class);
  }
}

Что я хотел бы сделать:

Validator v1 = injector.getInstance(Validator1.class);
Validator v2 = injector.getInstance(Validator2.class);

Возможно ли вообще?

4b9b3361

Ответ 1

Короткий ответ: привязка аннотаций. В основном это способ позволить разработчику дать подсказку, указывающую на конкретный экземпляр или реализацию, не требуя зависимости от полного конкретного класса реализации.

См: http://code.google.com/p/google-guice/wiki/BindingAnnotations

Например, в модуле вы можете:

bind(Validator.class).annotatedWith(ValidatorOne.class).to(OneOfMyValidators.class);
bind(Validator.class).annotatedWith(ValidatorTwo.class).to(SomeOtherValidator.class);

И в вашем конструкторе вы бы сделали:

@Inject
MyClass(@ValidatorOne Validator someValidator,
    @ValidatorTwo Validator otherValidator) {
  ...
}

Чтобы получить аннотированное значение прямо из Injector, вам нужно будет использовать класс Guice Key, например:

Validator v1 = injector.getInstance(Key.get(Validator.class, ValidatorOne.class));

На стороне примечания обязательные аннотации очень полезны для ввода констант времени выполнения. См. Комментарии для bindConstant в:

https://google.github.io/guice/api-docs/latest/javadoc/index.html?com/google/inject/Binder.html

Ответ 2

Я нашел этот поток при поиске решения для динамического связывания нескольких реализаций с интерфейсом, аналогичным ServiceLoader в Java. Ответ охватывает более общий случай, но он также может быть использован для получения конкретной реализации из набора. Multibinder позволяет связывать несколько реализаций с типом:

public class ValidatorsModule extends AbstractModule {
  protected void configure() {
      Multibinder<Validator> multibinder
          = Multibinder.newSetBinder(binder(), Validator.class);
      multibinder.addBinding().toInstance(new ValidatorOne());
      multibinder.addBinding().toInstance(new ValidatorTwo());
  }
}

//Usage
@Inject Set<Validator> validators;

Ответ 3

Очень похоже на предложение ejboy, но поскольку у вас есть разные классы Validator, вы можете привязываться к самим классам, не создавая экземпляры вручную.

protected void configure() {
   ...
   Multibinder<Validator> mb = Multibinder.newSetBinder(binder(), Validator.class);
   mb.addBinding().to(Validator1.class);
   mb.addBinding().to(Validator2.class);
   mb.addBinding().to(Validator3.class);
   ...
}

Затем рассматривается с точки зрения использования, например. Инъекция конструктора:

class UseCase {
    private Set<Validator> allOfThem;

    @Inject
    public UseCase(Set<Validator> allOfThem) {
        this.allOfThem = allOfThem;
        // e.g. iteratation
        for (Validator oneOfThem : allOfThem) {
            ...
        }
    }
}