【问题标题】:Retrieve a singleton by type and annotation using Guice使用 Guice 按类型和注释检索单例
【发布时间】:2013-09-23 20:39:36
【问题描述】:

我有几个实现interface Provider<Communication> 的类,我正在使用带有@Named 注释的Guice 根据需要绑定它们,例如:

@Singleton
public class Verizon implements Provider<Call> {
  ...
}

@Singleton
public class TMobile implements Provider<Call> {
  ...
}

bind (new TypeLiteral<Provider<Call>>() {}).annotatedWith(
  Names.named("Verizon")).to(Verizon.class);

bind (new TypeLiteral<Provider<Call>>() {}).annotatedWith(
  Names.named("TMobile")).to(TMobile.class);

有没有一种干净的方法来实现一个以名称为参数的工厂,例如:

public static <C extends Communication> Provider<C> getCallProvider(C communication) {
  String providerName = communication.getProviderName();

  return [Guice's matching object for type Provider<?> and @Named = providerName];
}

我尝试使用 Injector,但 Guice 不会将泛型作为 TypeLiteral 的参数:

public <C extends Communication> Provider<C> getCommunicationProvider(C communication) {
  return injector.getInstance(Key.get(new TypeLiteral<CommunicationProvider<C>>() {},
    Names.named(communication.getProvider().getId())));
}

这会抛出:

com.google.inject.ConfigurationException: Guice configuration errors:
  1) Provider<C> cannot be used as a key; It is not fully specified.

【问题讨论】:

    标签: java guice


    【解决方案1】:

    提供者由 Guice 管理;当您正确绑定FooProvider&lt;Foo&gt; 时,您应该能够请求FooProvider&lt;Foo&gt; 而无需任何额外工作。因此,您可能不希望这样:

    bind (new TypeLiteral<Provider<Call>>() {}).annotatedWith(
      Names.named("Verizon")).to(Verizon.class);
    

    相反,您可能想要这个:

    bind(Call.class).annotatedWith(Names.named("Verizon")).toProvider(Verizon.class);
    

    ...它可以让你注入@Named("Verizon") Provider&lt;Call&gt;,也可以注入@Named("Verizon") call。那时您的原始请求就像这样简单:

    /**
     * Because of erasure, at compile time the injector can only guarantee that it
     * returns something that extends Communication, not necessarily C. The cast and
     * @SuppressWarnings will help with that.
     */
    @SuppressWarnings("unchecked")
    public static <C extends Communication> Provider<C> getCommunicationProvider(
        C communication) {
      return (Provider<C>) injector.getProvider(Key.get(communication.getClass(),
          Names.named(communication.toString())));
    }
    

    还要注意,由于擦除,没有其他方法可以获取 C 类型的类文字,因此使用 Call 的模拟或动态代理将失败。

    如果您想绑定SomeOtherInterface&lt;Call&gt; 而不是Provider&lt;Call&gt;,您仍然可以这样做,但是您需要使用Guice 的Types util class 动态创建一个ParameterizedType 并将其用作Key#get(Type, Annotation) 的输入。有关创建 ParameterizedType 实现的更多背景信息,请阅读 this SO answer

    【讨论】:

      【解决方案2】:

      我认为这是不可能的。您可以自己编写工厂并将代码从使用接口更改为使用工厂。或者你可以将你的接口绑定到一个 Provder(但这会导致更多的代码而不是更少)。

      bind (new TypeLiteral<Provider<Call>>() {}).annotatedWith(
          Names.named("Verizon")).toProvider(new Provider<Provider<Call>>(){public Provider get(){return new Verizon();}});
      

      (或者您的提供者是 Guice 提供者?)

      【讨论】:

      • 这种方法的问题是工厂必须知道所有可能的类型 - 另一方面,我试图让事情尽可能通用。
      • 工厂应该知道,要创建什么......如果没有,请使用 Reflections-API 并使用命名模式来找到具体的实现。但我认为这比一个知道所有类的工厂更难看。
      猜你喜欢
      • 1970-01-01
      • 2011-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-17
      相关资源
      最近更新 更多