使用Guice注入通用实现

Sea*_*ter 47 java ioc-container guice typeliteral

我希望能够使用Guice注入通用接口的通用实现.

public interface Repository<T> {
  void save(T item);
  T get(int id);
}

public MyRepository<T> implements Repository<T> {
  @Override
  public void save(T item) {
    // do saving
    return item;
  }
  @Override
  public T get(int id) {
    // get item and return
  }
}
Run Code Online (Sandbox Code Playgroud)

在C#中使用Castle.Windsor,我可以做到:

Component.For(typeof(Repository<>)).ImplementedBy(typeof(MyRepository<>))
Run Code Online (Sandbox Code Playgroud)

但我不认为Guice中存在等价物.我知道我可以TypeLiteral在Guice 中使用来注册个人实现,但是有没有办法像Windsor那样一次注册它们?

编辑:

这是一个用法示例:

Injector injector = Guice.createInjector(new MyModule());
Repository<Class1> repo1 = injector.getInstance(new Key<Repository<Class1>>() {});
Repository<Class2> repo2 = injector.getInstance(new Key<Repository<Class2>>() {});
Run Code Online (Sandbox Code Playgroud)

虽然更可能的用法是注入另一个类:

public class ClassThatUsesRepository {
  private Repository<Class1> repository;

  @Inject
  public ClassThatUsesRepository(Repository<Class1> repository) {
    this.repository = repository;
  }
}
Run Code Online (Sandbox Code Playgroud)

Kde*_*per 65

为了在Guice中使用泛型,您需要使用TypeLiteral类来绑定泛型变体.这是Guice进样器配置的示例:

package your-application.com;

import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;

public class MyModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(new TypeLiteral<Repository<Class1>>(){})
      .to(new TypeLiteral<MyRepository<Class1>>(){});
  }
}
Run Code Online (Sandbox Code Playgroud)

(存储库是通用接口,MyRepository是通用实现,Class1是泛型中使用的特定类).

  • 这就是我的做法.我希望做的是消除注册每个单独实现的需要(MyRepository <Class1>,MyRepository <Class2>等).这就是温莎的例子. (6认同)
  • 对不起,我应该更仔细地阅读你的问题.我一直在研究这种类型的Guice泛型用法,但我也无法解决它.我想解决它的一种方法是扩展Guice并编写自己的模块(帮助器).通过使用Java反射API,您可以找到所有注入变体并绑定它们. (2认同)

SGa*_*Gal 5

在运行时没有保留的泛型确实使得首先掌握这个概念变得更加困难.无论如何,有理由new ArrayList<String>().getClass()返回Class<?>而不是Class<String>,虽然将其安全地投射给Class<? extends String>你应该记住,泛型只是用于编译时类型检查(有点像隐式验证,如果你愿意的话).

因此,如果您想MyRepository在需要新实例(任何类型)时使用Guice注入(使用任何类型)实现,Repository那么您根本不必考虑泛型,但是您可以自己确保类型安全(这就是为什么你得到那些讨厌的"未经检查"的警告).

这是一个代码工作得很好的例子:

public class GuiceTest extends AbstractModule {

    @Inject
    List collection;

    public static void main(String[] args) {
        GuiceTest app = new GuiceTest();
        app.test();
    }

    public void test(){
        Injector injector = Guice.createInjector(new GuiceTest());
        injector.injectMembers(this);

        List<String> strCollection = collection;
        strCollection.add("I'm a String");
        System.out.println(collection.get(0));

        List<Integer> intCollection = collection;
        intCollection.add(new Integer(33));
        System.out.println(collection.get(1));
    }

    @Override
    protected void configure() {
        bind(List.class).to(LinkedList.class);
    }
}
Run Code Online (Sandbox Code Playgroud)

这打印:

I'm a String
33
Run Code Online (Sandbox Code Playgroud)

但该名单由一个实现LinkedList.虽然在这个例子中,如果你试图将一个int作为String的东西,你会得到一个例外.

int i = collection.get(0)
Run Code Online (Sandbox Code Playgroud)

但是如果你想获得一个已经类型化的可注入对象,那么你可以要求List<String>而不仅仅是List,但是然后Guice会将该Type变量视为绑定键的一部分(类似于@Named之类的限定符).这意味着,如果你想注入明确 List<String>要的ArrayList<String>实施和List<Integer>是的LinkedList<Integer>,吉斯让你这样做(未测试,猜测).

但有一个问题:

    @Override
    protected void configure() {
        bind(List<String>.class).to(LinkedList<String>.class); <-- *Not Happening*
    }
Run Code Online (Sandbox Code Playgroud)

您可能会注意到类文字不是通用的.这就是你使用Guice的地方TypeLiterals.

    @Override
    protected void configure() {
        bind(new TypeLiteral<List<String>>(){}).to(new TypeLiteral<LinkedList<String>>(){});
    }
Run Code Online (Sandbox Code Playgroud)

TypeLiterals保留泛型类型变量作为元信息的一部分以映射到所需的实现.希望这可以帮助.