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

Guice: можно ли вводить модули?

У меня есть модуль, для которого требуется Depedency. Есть ли способ, которым сами модули могут быть введены? Я понимаю, что это немного ситуация с курицей и яйцом...

Пример:

public class MyModule implements Module {

    private final Dependency d_;

    @Inject public MyModule(Dependency d) {
        d_ = d;
    }

    public void configure(Binder b) { }

    @Provides Something provideSomething() {
        // this requires d_
    }
}

Я полагаю, что в этом случае решение было бы превратить метод @Provides в полноценный класс Provider<Something>. Это, очевидно, упрощенный пример; код, с которым я имею дело, имеет много таких методов @Provides, поэтому каждый из них разбивается на отдельные классы Provider<...>, а введение модуля для их настройки добавляет достаточное количество помех - и я думал, что Гис был связан с уменьшением беспорядочного кластера?

Возможно, это отражение моей относительной нуобильности для Guice, но я столкнулся с несколькими случаями, когда у меня возникло соблазн сделать это. Мне что-то не хватает...

4b9b3361

Ответ 1

@Provides методы могут принимать зависимости как параметры, аналогичные параметрам, для конструктора или метода @Inject:

@Provides Something provideSomething(Dependency d) {
   return new Something(d); // or whatever
}

Это документировано здесь, хотя, возможно, это может быть сделано, чтобы выделяться больше.

Ответ 2

Использование методов провайдера или @Provides велик, если вам нужна зависимость для ручной сборки объекта. Однако, что, если вам нужно что-то, чтобы помочь вам решить, как настроить сами привязки? Оказывается, вы можете использовать Guice для создания (и настройки) вашего модуля.

Вот пример (надуманный). Во-первых, модуль, который мы хотим настроить:

/**
 * Creates a binding for a Set<String> which represents the food in a pantry.
 */
public class PantryModule extends AbstractModule {
  private final boolean addCheese;

  @Inject
  public ConditionalModule(@Named("addCheese") boolean addCheese) {
    this.addCheese = addCheese;
  }

  @Override
  protected void configure() {
    Multibinder<String> pantryBinder = Multibinder
      .newSetBinder(binder(), String.class);

    pantryBinder.addBinding().toInstance("milk");

    if (addCheese) {
      pantryBinder.addBinding().toInstance("cheese");
    }

    pantryBinder.addBinding().toInstance("bread");
  }
}

PantryModule ожидает, что вводится логическое значение, чтобы решить, должен ли он включать сыр в кладовку.

Далее, мы будем использовать Guice для настройки модуля:

// Here we use an anonymous class as the "configuring" module. In real life, you would 
// probably use a standalone module.
Injector injector = Guice.createInjector(new AbstractModule() {
  @Override
  protected void configure() {
    // No cheese please!
    bindConstant().annotatedWith(Names.named("addCheese")).to(false);
    bind(PantryModule.class);
  }
});

Module configuredConditionalModule = injector.getInstance(PantryModule.class);

Теперь, когда у нас есть настроенный модуль, мы обновим наш инжектор, чтобы использовать его...

//...continued from last snippet...
injector = injector.createChildInjector(configuredConditionalModule);

И, наконец, мы получим набор строк, которые представляют нашу кладовку:

//...continued from last snippet...
Set<String> pantry = injector.getInstance(new Key<Set<String>>() {});

for (String food : pantry) {
  System.out.println(food);
}

Если вы поместите все фрагменты в основной метод и запустите его, вы получите следующий вывод:

milk
bread

Если вы измените привязку к "addCheese" с булевым значением "true", вы получите:

milk
cheese
bread

Этот метод классный, но, вероятно, полезен только тогда, когда вы контролируете экземпляр Injector и только тогда, когда модуль требует сложных зависимостей. Безусловно, я нашел настоящую потребность в этом в реальном проекте на работе. Если бы я это сделал, то кто-то еще мог бы тоже.

Ответ 3

Вопрос уже хорошо ответил, но я просто хотел добавить вариант к примеру Колина:

class MyModule extends AbstractModule { 
  public void configure() {
    bind(Something.class).toProvider(new Provider<Something>() {
       @Inject Dependency d;
       Something get() { return d.buildSomething(); }
    }
  }
}

Метод @Provides-метода более ясен, чем тот, который у меня выше для этого простого случая, но я обнаружил, что создание экземпляра реального провайдера может быть полезным и в некоторых ситуациях. Что-то я украл из списка рассылки; не приходило мне в голову;)

Ответ 4

В чем проблема с инициализацией модуля, просто вызвав new MyModule(d) или создав Provider<Something > , который имеет введенный Injector? Казалось бы, это стандартные способы решения этой проблемы. Как уже упоминалось, вы также можете использовать методы @Provides с аргументами.

Если зависимость не является обязательной, вы можете создать модуль, а затем вызвать установщик для инициализации значения, если это необходимо (например, com.google.inject.persist.jpa.JpaPersistModule делает это со свойствами, а при использовании new JpaPersistModule(String) - для загрузки правильной конфигурации).

В противном случае я полагаю, что это возможно (а затем вызовите createChildInjector(Modules... modules)), но я почти всегда предпочитаю один из других подходов к этому.