Guice: возможно ли вводить модули? - PullRequest
25 голосов
/ 31 марта 2011

У меня есть модуль, который требует немного 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 все сводил к уменьшению беспорядочного мусора?

Возможно, это отражение моего относительного невежества по отношению к Гисе, но я сталкивался с несколькими случаями, когда у меня возникало соблазн сделать это. Должно быть, я что-то упустил ...

Ответы [ 4 ]

28 голосов
/ 31 марта 2011
Методы

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

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

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

10 голосов
/ 20 октября 2012

Использование методов провайдера или @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);
}

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

milk
bread

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

milk
cheese
bread

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

5 голосов
/ 19 апреля 2011

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

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

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

0 голосов
/ 31 марта 2011

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

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

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

...