Guice 多注解
Guice multiple annotations
我有一个名为 StatsStore
的界面。我有这家商店的 2 个实现。内存中和 SQL 实现称为 InMemoryStatsStore
和 SqlStatsStore
。为了注入它们,我创建了 2 个注释 @InMemoryStore
和 @SqlStore
。注射是:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.to(InMemoryStatsStore.class);
bind(StatsStore.class)
.annotatedWith(SqlStore.class)
.to(SqlStatsStore.class);
现在我想添加一层新的注释来分隔 InMemoryStringStore
和 InMemoryNumberStore
但我不能在绑定行中添加多个注释,例如以下不编译:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.annotatedWith(NumberStoreAnnotation.class) // using named doesn't work as well
.to(InMemoryNumberStore.class);
如何在不使用单个命名注释的情况下添加多个注释,如果添加的层越多,就会变得非常复杂?
我想到的另一个解决方案是注入两次:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.to(InMemoryStatsStore.class);
bind(InMemoryStatsStore.class)
.annotatedWith(NumberStoreAnnotation.class)
.to(InMemoryNumberStore.class);
谢谢大家。
@BindingAnnotation
tells Guice that this is a binding annotation. Guice will produce an error if ever multiple binding annotations apply to the same member.
您可以改为使用命名绑定,或者您应该考虑重新设计您的解决方案。
正如 Amit 所说,您不能将多个 @BindingAnnotation 应用于任何给定的注入。在内部,Guice 的工作方式类似于 Map<Key, Provider>
,其中 Key 是可能参数化的 class,带有可选的单个注释实例。但是,因为这些是 个实例 ,欢迎您使用 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.) Equal annotations are defined 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
非常有效,因为您可以像注入注解一样调用注解,但是如何为 Names.named
这样的实例创建工厂方法?为此,您需要执行以下操作之一:
- Create an anonymous implementation, with accessors for each attribute as well as correct implementations of
equals
and hashCode
. Note that the hashCode
contract is much stricter than for Object
, but you can get compatible implementations from Apache annotation utils 或类似的库。
- 使用 AnnotationLiteral,它为任意子 classes 提供
equals
和 hashCode
实现。
- 使用 Google Auto 或类似的代码生成器为您生成兼容实现的代码。熟悉这种类型的解决方案对于 Android 和反射速度慢的其他内存受限环境特别有用,尽管此类环境通常会阻止您使用 Guice。 (
@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();
} // ...
}
}
我有一个名为 StatsStore
的界面。我有这家商店的 2 个实现。内存中和 SQL 实现称为 InMemoryStatsStore
和 SqlStatsStore
。为了注入它们,我创建了 2 个注释 @InMemoryStore
和 @SqlStore
。注射是:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.to(InMemoryStatsStore.class);
bind(StatsStore.class)
.annotatedWith(SqlStore.class)
.to(SqlStatsStore.class);
现在我想添加一层新的注释来分隔 InMemoryStringStore
和 InMemoryNumberStore
但我不能在绑定行中添加多个注释,例如以下不编译:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.annotatedWith(NumberStoreAnnotation.class) // using named doesn't work as well
.to(InMemoryNumberStore.class);
如何在不使用单个命名注释的情况下添加多个注释,如果添加的层越多,就会变得非常复杂?
我想到的另一个解决方案是注入两次:
bind(StatsStore.class)
.annotatedWith(InMemoryStore.class)
.to(InMemoryStatsStore.class);
bind(InMemoryStatsStore.class)
.annotatedWith(NumberStoreAnnotation.class)
.to(InMemoryNumberStore.class);
谢谢大家。
@BindingAnnotation
tells Guice that this is a binding annotation. Guice will produce an error if ever multiple binding annotations apply to the same member.
您可以改为使用命名绑定,或者您应该考虑重新设计您的解决方案。
正如 Amit 所说,您不能将多个 @BindingAnnotation 应用于任何给定的注入。在内部,Guice 的工作方式类似于 Map<Key, Provider>
,其中 Key 是可能参数化的 class,带有可选的单个注释实例。但是,因为这些是 个实例 ,欢迎您使用 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.) Equal annotations are defined 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
非常有效,因为您可以像注入注解一样调用注解,但是如何为 Names.named
这样的实例创建工厂方法?为此,您需要执行以下操作之一:
- Create an anonymous implementation, with accessors for each attribute as well as correct implementations of
equals
andhashCode
. Note that thehashCode
contract is much stricter than forObject
, but you can get compatible implementations from Apache annotation utils 或类似的库。 - 使用 AnnotationLiteral,它为任意子 classes 提供
equals
和hashCode
实现。 - 使用 Google Auto 或类似的代码生成器为您生成兼容实现的代码。熟悉这种类型的解决方案对于 Android 和反射速度慢的其他内存受限环境特别有用,尽管此类环境通常会阻止您使用 Guice。 (
@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();
} // ...
}
}