Guice 多个注释

Guy*_*rin 5 java guice

我有一个名为 的接口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)

现在我想添加一层新的注释来分隔 和 ,InMemoryStringStoreInMemoryNumberStore我无法向绑定行添加多个注释,例如以下内容无法编译:

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)

谢谢大家。

Jef*_*ica 5

正如 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?为此,您需要执行以下操作之一:

  1. 创建一个匿名实现,其中包含每个属性的访问器以及 和 的正确equals实现hashCode。请注意,该hashCode契约比 for 严格得多Object,但您可以从Apache 注释实用程序或类似库获得兼容的实现。
  2. 使用AnnotationLiteral,它为任意子类提供equalshashCode实现。
  3. 使用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();
    } // ...
  }
}
Run Code Online (Sandbox Code Playgroud)