Java 使用 Guice 注入通用实现
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4238919/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Inject Generic Implementation using Guice
提问by Sean Carpenter
I would like to be able to inject a generic implementation of a generic interface using Guice.
我希望能够使用 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
}
}
In C# using Castle.Windsor, I'd be able to do:
在 C# 中使用 Castle.Windsor,我可以做到:
Component.For(typeof(Repository<>)).ImplementedBy(typeof(MyRepository<>))
but I don't think the equivalent exists in Guice. I know I can use TypeLiteral
in Guice to register individual implementations, but is there any way to register them all at once like in Windsor?
但我认为 Guice 中不存在等价物。我知道我可以TypeLiteral
在 Guice中使用来注册单个实现,但是有没有办法像在 Windsor 中一样一次性注册它们?
Edit:
编辑:
Here's an example of usage:
下面是一个使用示例:
Injector injector = Guice.createInjector(new MyModule());
Repository<Class1> repo1 = injector.getInstance(new Key<Repository<Class1>>() {});
Repository<Class2> repo2 = injector.getInstance(new Key<Repository<Class2>>() {});
Although the more likely usage would be injection into another class:
虽然更有可能的用法是注入另一个类:
public class ClassThatUsesRepository {
private Repository<Class1> repository;
@Inject
public ClassThatUsesRepository(Repository<Class1> repository) {
this.repository = repository;
}
}
回答by Kdeveloper
In order to use generics with Guice you need to use the TypeLiteralclass to bind the generic variants. This is an example of how you're Guice injector configuration could look like:
为了在 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>>(){});
}
}
(Repository is the generic interface, MyRepository is the generic implementation, Class1 is the specific class used in the generics).
(Repository 是泛型接口,MyRepository 是泛型实现,Class1 是泛型中使用的具体类)。
回答by SGal
Generics not being retained at run-time sure made it harder to grasp the concept at first.
Anyways, there are reasons new ArrayList<String>().getClass()
returns Class<?>
and not Class<String>
and although its safe to cast it to Class<? extends String>
you should remember that generics are there just for compile-time type checks (sort of like implicit validation, if you will).
没有在运行时保留的泛型肯定会让一开始更难理解这个概念。无论如何,有new ArrayList<String>().getClass()
返回Class<?>
而不是返回的原因Class<String>
,尽管可以安全地将其转换为Class<? extends String>
您应该记住泛型仅用于编译时类型检查(有点像隐式验证,如果您愿意的话)。
So if you want to use Guice to inject MyRepository
(with any type) implementation whenever you need a new instance of Repository
(with any type) then you don't have to think about generics at all, but you're on your own to ensure type safety (that's why you get those pesky "unchecked" warning).
因此,如果您想在需要MyRepository
(具有任何类型)的新实例时使用 Guice 注入(具有任何类型)实现,Repository
那么您根本不必考虑泛型,但您需要自己确保类型安全(这就是为什么你会收到那些讨厌的“未经检查”的警告)。
Here is an example of code working just fine:
这是一个工作正常的代码示例:
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);
}
}
This prints:
这打印:
I'm a String
33
But that list isimplemented by a LinkedList
. Although in this example, if you tried to asign an intsomething that is Stringyou would get an exception.
但是该列表是由LinkedList
. 虽然在这个例子中,如果你试图ASIGN的INT东西是字符串,你会得到一个异常。
int i = collection.get(0)
But if you want to get an injectable object already type-casted and dandy you can ask for List<String>
instead of just List, but then Guice will treat that Type variable as part of the binding key (similar to a qualifier such as @Named). What this means is that if you want injection specifically List<String>
to be of ArrayList<String>
implementation and List<Integer>
to be of LinkedList<Integer>
, Guice lets you do that (not tested, educated guess).
但是如果你想获得一个已经类型转换的可注入对象,你可以要求List<String>
而不仅仅是 List,但是 Guice 会将该类型变量视为绑定键的一部分(类似于诸如@Named 的限定符)。这意味着,如果您希望注入专门 List<String>
用于ArrayList<String>
实现并List<Integer>
成为LinkedList<Integer>
,Guice 可以让您这样做(未经测试,有根据的猜测)。
But there's a catch:
但有一个问题:
@Override
protected void configure() {
bind(List<String>.class).to(LinkedList<String>.class); <-- *Not Happening*
}
As you might notice class literals aren't generic. That's where you use Guice's TypeLiterals
.
您可能会注意到类文字不是通用的。这就是您使用 Guice 的TypeLiterals
.
@Override
protected void configure() {
bind(new TypeLiteral<List<String>>(){}).to(new TypeLiteral<LinkedList<String>>(){});
}
TypeLiterals
retain the generic type variable as part of meta-information to map to desired implementation. Hope this helps.
TypeLiterals
保留泛型类型变量作为元信息的一部分以映射到所需的实现。希望这可以帮助。
回答by jedediah
You can use (abuse?) the @ImplementedBy
annotation to make Guice generate generic bindings for you:
您可以使用(滥用?)@ImplementedBy
注释让 Guice 为您生成通用绑定:
@ImplementedBy(MyRepository.class)
interface Repository<T> { ... }
class MyRepository<T> implements Repository<T> { ... }
As long as just-in-time bindings are enabled, you can inject Repository<Whatever>
without any explicit binding:
只要启用了即时绑定,您就可以在Repository<Whatever>
没有任何显式绑定的情况下注入:
Injector injector = Guice.createInjector();
System.out.println(injector.getBinding(new Key<Repository<String>>(){}));
System.out.println(injector.getBinding(new Key<Repository<Integer>>(){}));
The catch is that the target of the binding is MyRepository
, rather than MyRepository<T>
:
问题是绑定的目标是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]]}
That's usually not a problem, but it means that MyRepository
can't inject a TypeLiteral<T>
to figure out its own type at runtime, which would be particularly useful in this situation. Aside from that, to the best of my knowledge, this works fine.
这通常不是问题,但这意味着MyRepository
不能TypeLiteral<T>
在运行时注入 a来确定它自己的类型,这在这种情况下特别有用。除此之外,据我所知,这很好用。
(If someone feels like fixing this, I'm pretty sure it would just require some extra calculations around hereto fill in the target type parameters from the source key.)
(如果有人想解决这个问题,我很确定它只需要在这里进行一些额外的计算来填充源键中的目标类型参数。)
回答by ohad serfaty
Somewhat related, hopefully someone will find this useful. In some cases, especially when you have the java.lang.Class instance of the type you want to generalize, it's possible to force an injection in runtime by extending the ParameterizedType class.
有点相关,希望有人会发现这很有用。在某些情况下,尤其是当您拥有要泛化的类型的 java.lang.Class 实例时,可以通过扩展 ParameterizedType 类在运行时强制注入。
In the solution below, a factory method creates a generic Collection< ? extends Number>and Map < K,V >given an instance of the class object
在下面的解决方案中,工厂方法创建了一个通用的Collection< ? extends Number>和Map < K,V >给定一个类对象的实例
Example.java:
例子.java:
@SuppressWarnings("unchecked")
public class Example<K extends Number> {
Injector injector = ...
public Set<K> foo(Class<K> klass) {
CompositeType typeLiteral = new CompositeType(Set.class, klass);
Set<K> set = (Set<K>) injector.getInstance(Key.get(typeLiteral));
return set;
}
public <V> Map<K,V> bar(Class<K> keyClass, Class<V> valueClass) {
CompositeType typeLiteral = new CompositeType(Map.class, keyClass, valueClass);
Map<K,V> collection = (Map<K,V>) injector.getInstance(Key.get(typeLiteral));
return collection;
}
}
CompositeType.java:
复合类型.java:
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
public class CompositeType implements ParameterizedType {
private final String typeName;
private final Class<?> baseClass;
private final Type[] genericClass;
public CompositeType(Class<?> baseClass, Class<?>... genericClasses) {
this.baseClass = baseClass;
this.genericClass = genericClasses;
List<String> generics = ((List<Class<?>>)Arrays.asList(genericClasses))
.stream()
.map(Class::getName)
.collect(Collectors.toList());
String genericTypeString = StringUtils.join(generics, ",");
this.typeName = baseClass.getName() + "<" + genericTypeString + ">";
}
@Override
public String getTypeName() {
return typeName;
}
@Override
public Type[] getActualTypeArguments() {
return genericClass;
}
@Override
public Type getRawType() {
return baseClass;
}
@Override
public Type getOwnerType() {
return null;
}
}