Передать параметр в конструктор с помощью 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();
Вот что мы в итоге сделали. Это плохое решение, которое следует использовать только тогда, когда у вас нет времени и вы просто хотите дожить до следующего дня.
- Удалите все конструкторы, сделав класс конструируемым по умолчанию.
- Добавьте общедоступный метод с именем or such, который принимает зависимости в качестве параметров и присваивает их полям.
- Используйте Guice
Injector.getInstance()
чтобы выполнить обнаружение и создание класса за вас, затем вызовите метод с полями, которые вам действительно нужны.
Некоторое прощение за это можно получить, выполнив агрессивную проверку нуля, которая информирует не обращающих внимания вызывающих абонентов о необходимости позвонить.init
бросая соответствующим образом сформированныйIllegalStateException
.