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

Гик-помощь вводится глубже по иерархии зависимостей

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

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

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

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

Но я действительно хочу что-то вроде этого:

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

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

4b9b3361

Ответ 1

Способ обмана: Придерживайтесь input в статической переменной или singleton 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 доступной для всего графика объекта развязанным способом.

Ответ 2

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

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

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

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