Как использовать Guice AssistedInject с несколькими реализациями интерфейса?
У меня проблемы с поиском, как иметь "динамический AssistedInject". Под этим я подразумеваю, что я хотел бы предоставить фабрике имя реализующего класса, которое необходимо использовать во время выполнения, на основе параметра. Вот что у меня сейчас есть:
interface Letter {}
abstract class ALetter implements Letter {
public ALetter(T1 t1, T2 t2) {...}
}
class A implements Letter {
public static final NAME = "A";
@Inject
public A(@Assisted T1 t1, T2 t2) { super(t1, t2); }
}
class B implements Letter {
public static final NAME = "B";
@Inject
public B(@Assisted T1 t1, T2 t2) { super(t1, t2); }
}
Я хотел бы иметь возможность загружать либо A, либо B в зависимости от их имени, учитывая, что у меня будет T1, но мне понадобится T2. Это может выглядеть примерно так:
class MyClass {
@Inject
public MyClass(RequestData data, MyConfig config, ILetterFactory letterFactory) {
Letter letter = letterFactory.get(data.getLetterName(), config.getT2());
}
}
И я бы настроил ILetterFactory, используя что-то вроде:
install(new FactoryModuleBuilder().build(ILetterFactory.class));
Но я знаю, что в настоящее время это не сработает, поскольку lettername не является реальным параметром моего конструктора, а Guice просто так не работает. Это должно дать вам представление о том, чего я хочу.
Единственное решение, которое я нашел в настоящее время, не использует Guice: у меня есть собственная фабрика, которая разрешает конструктор класса на основе имени (через Map<String, Constructor>
) и звонки newInstance
предоставляя ему соответствующие параметры (вообще никаких инъекций, кроме как на самой фабрике).
Есть ли способ использовать Guice, чтобы сам не создавать эту фабрику? Спасибо
1 ответ
FactoryModuleBuilder не такой мощный, как вы думаете, - у него нет возможности переключаться между реализациями, и он используется только для смешивания введенных зависимостей с другими аргументами конструктора. К счастью, Guice облегчает написание вашей фабрики. То, что вы ищете, является реализацией этого интерфейса:
public interface ILetterFactory {
/** This assumes that T2 is a Guice-satisfied dependency,
* and not meant to be @Assisted, so leave it out of the interface. */
Letter get(String which);
}
Первый вариант - позволить Guice предоставлять ваши экземпляры с провайдером, что наиболее полезно, если в ваших письмах широко различаются депы, или если ваш список деппов длинный или часто меняется. Если A и B нужны вспомогательные зависимости, замените Provider<A>
с A.Factory
что вы связаны через FactoryModuleBuilder.
public class LetterFactory implements ILetterFactory {
@Inject Provider<A> aProvider;
@Inject Provider<B> bProvider;
@Override public Letter get(String which) {
if (A.NAME.equals(which)) {
return aProvider.get();
} else if (B.NAME.equals(which)) {
return bProvider.get();
} else {
throw new IllegalArgumentException("Letter does not exist");
}
}
}
Выбрав второй вариант, вы берете на себя ответственность за создание нового экземпляра из Guice. Это требует больше обслуживания, если ваш список deps меняется, и не так хорошо работает с AOP и другими подобными функциями, но может быть немного меньше работы, если ваши письма нуждаются в вспомогательных параметрах и небольшом наборе похожих deps:
public class LetterFactory implements ILetterFactory {
@Inject Provider<T2> t2Provider;
@Override public Letter get(String which) {
if (A.NAME.equals(which)) {
return new A(t2Provider.get());
} else if (B.NAME.equals(which)) {
return new B(t2Provider.get());
} else {
throw new IllegalArgumentException("Letter does not exist");
}
}
}