我有一个名为 的接口StatsStore。我有这家商店的 2 个实现。InMemoryStatsStore一个名为and的内存中 SQL 实现SqlStatsStore。为了注入它们,我创建了 2 个注释@InMemoryStore和@SqlStore. 注射是:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.to(InMemoryStatsStore.class);
bind(StatsStore.class)
.annotatedWith(SqlStore.class)
.to(SqlStatsStore.class);
Run Code Online (Sandbox Code Playgroud)
现在我想添加一层新的注释来分隔 和 ,InMemoryStringStore但InMemoryNumberStore我无法向绑定行添加多个注释,例如以下内容无法编译:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.annotatedWith(NumberStoreAnnotation.class) // using named doesn't work as well
.to(InMemoryNumberStore.class);
Run Code Online (Sandbox Code Playgroud)
如何在不使用单个命名注释的情况下添加多个注释,添加的层越多,这将变得相当复杂?
我想到的另一个解决方案是注入两次:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.to(InMemoryStatsStore.class);
bind(InMemoryStatsStore.class)
.annotatedWith(NumberStoreAnnotation.class)
.to(InMemoryNumberStore.class);
Run Code Online (Sandbox Code Playgroud)
谢谢大家。
正如 Amit 所说,您不能将多个 @BindingAnnotation 应用于任何给定的注入。在内部,Guice 的工作方式就像Map<Key, Provider>一个 Key 是一个可能参数化的类,带有可选的单个注释实例。但是,因为这些是实例,所以欢迎您创建自己的可实例化注释,以按其工作方式Named工作。
@Inject @InMemoryStore(NUMBER) StatsStore inMemoryNumberStore;
@Inject @SqlStore(STRING) StatsStore sqlStringStore;
// or
@Inject @Store(dataType=NUMBER, backend=SQL) sqlNumberStore;
Run Code Online (Sandbox Code Playgroud)
注释必须具有像这样定义的字段。(如果您有一个名为 的元素value,则可以根据 JLS 9.7.3省略属性名称。)等效注释的定义如文档中Annotation.equals所示。
public enum DataType { NUMBER, STRING; }
public enum Backend { SQL, IN_MEMORY; }
@BindingAnnotation @Retention(SOURCE) @Target({ FIELD, PARAMETER, METHOD })
public @interface Store {
DataType dataType();
Backend backend();
}
Run Code Online (Sandbox Code Playgroud)
@Provides当您可以像注入注释一样调用注释时,这非常有效,但是如何为像这样的实例创建工厂方法呢Names.named?为此,您需要执行以下操作之一:
equals实现hashCode。请注意,该hashCode契约比 for 严格得多Object,但您可以从Apache 注释实用程序或类似库获得兼容的实现。equals和hashCode实现。@Qualifier注释在其他 JSR-330 兼容的依赖注入框架中的工作方式相同,包括 Dagger。)如果上面的内容看起来有点复杂,或者如果您想要比 Guice 基于映射的实现可以完成的更复杂的逻辑,一种替代方法是添加一个您控制的间接层:
public class StoreStore {
@Inject Provider<InMemoryNumberStore> inMemoryNumberStoreProvider;
// ...
// You can also inject the Injector to call getInstance with a class literal.
public StatsStore getStore(DataType dataType, Backend backend) {
// This can also be a switch or any other sort of lookup, of course.
if (dataType == NUMBER && backend == IN_MEMORY) {
return inMemoryNumberStoreProvider.get();
} // ...
}
}
Run Code Online (Sandbox Code Playgroud)