使用Guice注入通用实现

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

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 } } 

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

 Component.For(typeof(Repository<>)).ImplementedBy(typeof(MyRepository<>)) 

但我不认为在Guice中存在相同的东西。 我知道我可以在Guice中使用TypeLiteral来注册各个实现,但是有没有办法象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>>() {}); 

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

 public class ClassThatUsesRepository { private Repository<Class1> repository; @Inject public ClassThatUsesRepository(Repository<Class1> repository) { this.repository = repository; } } 

为了在Guice中使用generics,需要使用TypeLiteral类来绑定generics变体。 这是Guice喷油器configuration如何的例子:

 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>>(){}); } } 

(Repository是generics接口,MyRepository是generics实现,Class1是generics中使用的特定类)。

在运行时不被保留的generics确实使得最初难以理解概念。 无论如何,有原因new ArrayList<String>().getClass()返回Class<?>而不是Class<String> ,虽然它安全地将其转换为Class<? extends String> Class<? extends String>你应该记住,generics只是为了编译时types检查(有点像隐式validation,如果你愿意的话)。

所以,如果你想要使用Guice来注入MyRepository (任何types)的实现,只要你需要一个新的Repository实例(任何types),那么你就不必考虑generics了,但是你自己去确保types安全(这就是为什么你得到那些讨厌的“未检查”警告)。

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

 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); } } 

这打印:

 I'm a String 33 

但是这个列表LinkedList实现的。 虽然在这个例子中,如果你尝试赋值一个inttypes的东西,你会得到一个exception。

 int i = collection.get(0) 

但是,如果你想获得一个注入对象已经被input和花花公子,你可以要求List<String>而不是List,但是Guice将把Typevariables作为绑定键的一部分(类似于一个限定符如@命名)。 这意味着如果你想特别注意List<String>ArrayList<String>实现,并且List<Integer>LinkedList<Integer> ,那么Guice允许你这样做(没有经过testing,受过良好教育的猜测)。

但有一个问题:

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

正如你可能注意到类文字不是通用的 。 那就是你使用Guice的TypeLiteralsTypeLiterals

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

TypeLiterals将genericstypesvariables保留为元信息的一部分,以映射到所需的实现。 希望这可以帮助。

您可以使用(滥用?) @ImplementedBy注释让Guice为您生成通用绑定:

 @ImplementedBy(MyRepository.class) interface Repository<T> { ... } class MyRepository<T> implements Repository<T> { ... } 

只要启用了即时绑定,就可以在没有任何显式绑定的情况下注入Repository<Whatever>

  Injector injector = Guice.createInjector(); System.out.println(injector.getBinding(new Key<Repository<String>>(){})); System.out.println(injector.getBinding(new Key<Repository<Integer>>(){})); 

问题是绑定的目标是MyRepository ,而不是MyRepository<T>

 LinkedKeyBinding{key=Key[type=Repository<java.lang.String>, annotation=[none]], source=interface Repository, scope=Scopes.NO_SCOPE, target=Key[type=MyRepository, annotation=[none]]} LinkedKeyBinding{key=Key[type=Repository<java.lang.Integer>, annotation=[none]], source=interface Repository, scope=Scopes.NO_SCOPE, target=Key[type=MyRepository, annotation=[none]]} 

这通常不是问题,但是这意味着MyRepository不能注入TypeLiteral<T>来在运行时找出它自己的types,这在这种情况下特别有用。 除此之外,据我所知,这工作正常。

(如果有人想解决这个问题,我敢肯定,只需要在这里附加一些额外的计算来从源代码中填入目标types参数。)