Передать параметр в конструктор с помощью Guice

У меня есть фабрика, как показано ниже,

public final class Application {

    private static IFoo foo;

    public static IFoo getFoo(String bar)
    {
            // i need to inject bar to the constructor of Foo
            // obvious i have to do something, not sure what
        Injector injector = Guice.createInjector();
        logger = injector.getInstance(Foo.class);
        return logger;              
    }

}

Это определение Foo:

class Foo
{
   Foo(String bar)
   {

   }

}

ХОРОШО. Я не уверен, как я могу передать этот параметр конструктору Foo с помощью Guice?

Есть идеи?

6 ответов

Решение

Все ответы "Guice Constructor Parameter" в некотором роде кажутся неполными. Вот полное решение, включая использование:

interface FooInterface{
  String getFooName();
}

// Аннотируем конструктор и вспомогательные параметры в классе реализации

class Foo implements FooInterface {
   String bar;

   @Inject
   Foo(@Assisted String bar)
   {
      this.bar = bar;
   }

   // return the final name
   getFooName(){
     return this.bar;
   }

}

// Создать заводской интерфейс с методом create(), который принимает только вспомогательные параметры. // Интерфейс FooFactory не имеет явного класса реализации (Guice Magic)

interface FooFactory{
   Foo create(String bar);
}

// Привязать эту фабрику к провайдеру, созданному AssistedInject

binderModule implements Module{

 void configure(Binder binder) {
   binder.install(new FactoryModuleBuilder()
         .implement(FooInterface.class, Foo.class)
         .build(FooFactory.class));
 }
}

// Теперь используем это:

class FooAction{

   @Inject private FooFactory fooFactory;

   doFoo(){
      // Send bar details through the Factory, not the "injector" 
      Foo f = fooFactory.create("This foo is named bar. How lovely!");
      f.getFooName(); // "This foo is named bar. How lovely!"
   }
}

Здесь много полезной информации: https://google.github.io/guice/api-docs/latest/javadoc/index.html?com/google/inject/assistedinject/FactoryModuleBuilder.html

То, что вы, вероятно, ищете, это использовать фабрику Guice. Особенно легко с AssistedInject функциональность, но у них есть ручной пример в верхней части страницы. Суть этого примера в том, что вы получаете фабрику в нестатическом режиме. getFoo метод, которым вы передаете все необходимые параметры и строите объект оттуда.

Это не будет работать напрямую, если у вас есть перехват метода в Foo, но это будет работать во многих других случаях.

Использовать AssistedInject, который для меня имеет более чистую семантику и означает меньше ручного подключения, вам понадобится расширение guice-assistedinject в classpath, тогда при создании Foo (Что ж, FooImpl мы должны использовать интерфейсы):

@Inject
public FooImpl(@Assisted String bar)
{
    this.baz = bar;
}

Затем вы создаете FooFactory интерфейс:

public interface FooFactory {
    public Foo create(String bar);
}

Тогда в вашем модуле Guice:

install(new FactoryModuleBuilder()
    .implement(Foo.class, FooImpl.class)
    .build(FooFactory.class));

Вы можете проверить Javadoc для FactoryModuleBuilder для примеров с более сложными фабриками.

Я знаю, что это старая ветка, но сегодня я сам решил эту проблему. Мне нужно всего два или максимум три разных экземпляра 'Foo', и я действительно не хотел писать весь болгарный код Factory. Немного погуглив, я нашел этот " Стаббизмы" - блог Тони. Я бы предложил это решение, которое идеально подходит, если вы точно знаете, какие экземпляры вам нужны.

В модуле Guice:

    bind(Foo.class).annotatedWith(Names.named("firstFoo")).toProvider(new Provider<Foo>() {
        @Override
        public Foo get() {
            return new FooImpl("topic A");
        }
    });
    bind(Foo.class).annotatedWith(Names.named("secondFoo")).toProvider(new Provider<Foo>() {
        @Override
        public Foo get() {
            return new FooImpl("topic B");
        }
    });

Или в Java 8:

    bind(Foo.class).annotatedWith(Names.named("firstFoo")).toProvider(() -> new FooImpl("first"));
    bind(Foo.class).annotatedWith(Names.named("secondFoo")).toProvider(() -> new FooImpl("second"));

И в конструкторе вашего сервиса, где вам нужны экземпляры Foo:

@Inject
public MyService (
    @Named("firstFoo") Foo firstFoo,
    @Named("secondFoo") Foo secondFoo) {
}

И Фу в моем случае:

public class FooImpl implements Foo {

    private String name;

    public FooImpl(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
}

Надеюсь, это поможет кому-то.

Если этот класс является фабрикой, это должен быть объект, управляемый Guice, имеющий нестатический метод getFoo, а метод getFoo просто использует

new Foo(bar)

Не каждый класс должен быть создан Guice.

Также см. AssistedInject, чтобы избежать создания этой фабрики самостоятельно, и пусть Guice создаст ее для вас.

Хотя это не прямой ответ на ваш вопрос, надеюсь, это поможет. Раньше я пытался понять, куда передаются параметры конструктора. Если это пользовательские классы, они должны быть привязаны к модулю.

      Class CustomHandler {

  private Config config;

  @Inject
  CustomHandler(Config config) {
    this.config = config;
  }

  public void handle() {
    // handle using config here
  }
}

Привязка:

      class Module extends AbstractModule {
  bind(Handler.class).to(CustomHandler.class);
  bind(Config.class).to(CustomConfig.class);
}

Инъекция:

      CustomHandler handler = injector.getInstance(CustomHandler.class);
handler.handle();

Вот что мы в итоге сделали. Это плохое решение, которое следует использовать только тогда, когда у вас нет времени и вы просто хотите дожить до следующего дня.

  1. Удалите все конструкторы, сделав класс конструируемым по умолчанию.
  2. Добавьте общедоступный метод с именем or such, который принимает зависимости в качестве параметров и присваивает их полям.
  3. Используйте GuiceInjector.getInstance()чтобы выполнить обнаружение и создание класса за вас, затем вызовите метод с полями, которые вам действительно нужны.

Некоторое прощение за это можно получить, выполнив агрессивную проверку нуля, которая информирует не обращающих внимания вызывающих абонентов о необходимости позвонить.initбросая соответствующим образом сформированныйIllegalStateException.

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