我有一个名为 StatsStore
的界面。我有这家商店的 2 个实现。称为 InMemoryStatsStore
和 SqlStatsStore
的内存中和 SQL 实现。为了注入(inject)它们,我创建了 2 个注释 @InMemoryStore
和 @SqlStore
。注入(inject)是:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.to(InMemoryStatsStore.class);
bind(StatsStore.class)
.annotatedWith(SqlStore.class)
.to(SqlStatsStore.class);
现在我想添加一个新的注释层来分隔 InMemoryStringStore
和 InMemoryNumberStore
但我不能向绑定(bind)行添加多个注释,例如以下不编译:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.annotatedWith(NumberStoreAnnotation.class) // using named doesn't work as well
.to(InMemoryNumberStore.class);
如何在不使用单个命名注解的情况下添加多个注解,如果添加的层数越多,注解会变得非常复杂?
我想到的另一个解决方案是注入(inject)两次:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.to(InMemoryStatsStore.class);
bind(InMemoryStatsStore.class)
.annotatedWith(NumberStoreAnnotation.class)
.to(InMemoryNumberStore.class);
谢谢大家。
最佳答案
正如 Amit 所说,您不能将多个 @BindingAnnotation 应用于任何给定的注入(inject)。在内部,Guice 的工作方式类似于 Map<Key, Provider>
其中 Key 是一个可能参数化的类,带有一个可选的单个注释实例。但是,因为这些是实例,欢迎您使用 create your own instantiable annotation工作方式Named
有效。
@Inject @InMemoryStore(NUMBER) StatsStore inMemoryNumberStore;
@Inject @SqlStore(STRING) StatsStore sqlStringStore;
// or
@Inject @Store(dataType=NUMBER, backend=SQL) sqlNumberStore;
注释必须像这样定义字段。 (如果您有一个名为 value
的元素,则可以省略属性名称 per JLS 9.7.3 。)相等的注释定义为 as in the Annotation.equals
docs .
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();
}
这很适合 @Provides
,当你可以像注入(inject)注解一样调用注解时,但是如何为 Names.named
这样的实例创建工厂方法呢? ?为此,您需要执行以下操作之一:
- Create an anonymous implementation ,每个属性的访问器以及
equals
的正确实现和hashCode
.请注意hashCode
契约(Contract)是much stricter than forObject
, 但您可以从 Apache annotation utils 获得兼容的实现或类似的库。 - 使用AnnotationLiteral , 它提供了
equals
和hashCode
任意子类的实现。 - 使用Google Auto或类似的代码生成器为您生成兼容实现的代码。熟悉这种类型的解决方案对于 Android 和其他反射速度慢的内存受限环境特别有用,尽管此类环境通常会阻止您使用 Guice。 (尽管如此,
@Qualifier
注释在其他 JSR-330 兼容依赖注入(inject)框架中的工作方式相同,包括 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();
} // ...
}
}
关于java - Guice 多注解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39604399/