Внедрение с помощью Guice вглубь иерархии зависимостей

Я хочу провести цепочку элементов обработки и соединить их вместе через Guice. Давайте предположим следующий путь:

  • interface A осуществляется class AImpl нужен некоторый вклад
  • interface B осуществляется class BImpl потребности A
  • interface C осуществляется class CImpl потребности B
  • interface D осуществляется class DImpl потребности C

Зависимость A может быть разрешена только во время выполнения, а не во время настройки. Обычный подход заключается в использовании Assisted Injection в этом случае для создания фабрики, которая принимает отсутствующие экземпляры в качестве параметров, вот так:

public interface AFactory {
    public A createA(String input);
}

Но то, что я на самом деле хочу, это что-то вроде этого:

public interface DFactory {
    public D createD(String inputForA);
}

Я не хочу проходить вручную AImpl-специфичные зависимости по всей иерархии. Можно ли добиться этого с Guice? Если нет, то как лучше элегантно обойти эту проблему, сохраняя при этом преимущества инъекций?

2 ответа

Решение

Обман: Палка input в статической переменной или синглтоне ThreadLocal, Установите его до начала конвейера и очистите после его завершения. Свяжите все остальное через DI.

Причудливый путь: в Aобратитесь к @PipelineInput String inputString но не связывайте это в своем главном инжекторе. В противном случае, связывайте зависимости как обычно, включая ссылку на @PipelineInput в других классах, связанных с трубопроводом. Когда вам нужно Dполучить его от вашей реализации DFactory, которому я звоню PipelineRunner,

public class PipelineRunner {
  @Inject Injector injector; // rarely a good idea, but necessary here

  public D createD(final String inputForA) {
    Module module = new AbstractModule() {
      @Override public void configure() {
        bindConstant(inputForA).annotatedWith(PipelineInput.class);
      }
    };
    return injector.createChildInjector(new PipelineModule(), module)
        .getInstance(D.class);
  }
}

Естественно, попытки связывания для A, B, C, а также D потерпит неудачу за пределами PipelineRunner из-за отсутствия @PipelineInput String- ты получишь CreationException когда вы создаете инжектор с этими неудовлетворенными зависимостями, как вы обнаружили, но эти конвейерные зависимости должны быть легко разделены на модуль, который вы устанавливаете в дочерний инжектор.

Если это кажется слишком хакерским, помните, что PrivateModules также " реализованы с использованием родительских инжекторов", и что весь смысл внедрения зависимости заключается в том, чтобы создать зависимость, подобную inputForA доступный для всего графа объекта в развязанном виде.

Я вижу три варианта. Они зависят от того, как часто вы меняете input за A,

1) Bind input как константа в вашем модуле. Это работает, только если вы знаете это значение до того, как создадите Injector и никогда не хочу менять значение. Увидеть bindConstant

2) Используйте частный подмодуль, который связывает A или значение для input внутри этого модуля. В основном вы можете иметь два или три экземпляра графа с разными значениями. Смотрите newPrivateBinder.

3) Используйте Scope ала RequestScope, SessionScope,... Таким образом, вы можете часто менять ввод, но вы должны войти / выйти из области в некоторой точке, которая будет определена. См. Пользовательские области для примера.

Другие вопросы по тегам