如何使用 Guice 注入索引(和特定于类)的字符串



我最近一直在与Google Guice进行黑客攻击,我想出了一个想法,根据声明构造函数的类和注释中定义的其他几个参数,将String注入构造函数。例如: 如果我定义一个新的限定符注释@NamedInjectable供 Guice 使用:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Qualifier
public @interface NamedInjectable
{
String name() default "";
boolean indexed() default true;
}

其中 name 是字符串的新名称基础(默认仅为类的名称),indexed表示每次注入新字符串时是否应递增名称。
例如

public MyClass {
@Inject
public MyClass(@NamedInjectable(name = "foo", indexed = true) String name) {
// some code
}
}

并且name参数应该被赋予一个值,例如" 我考虑过使用提供程序绑定辅助注入,但我可以完成它。失败的一个主要原因是以某种方式获取了类的名称。

你还有其他想法吗?

没有内置方法可以根据名称自定义标准 Guice 绑定。如果你想坚持只使用Guice,你可能需要自定义注射。

除了标准的@Inject驱动进样外,Guice 还包括用于定制进样的钩子。这使Guice能够托管具有自己的注入语义或注释的其他框架。大多数开发人员不会直接使用自定义注入;但他们可能会在扩展和第三方库中看到它们的使用。每个自定义注入都需要一个类型侦听器、一个注入侦听器和每个类型的注册。

Guice 的自定义注入文档示例演示了一个使用注入类的类型自定义的记录器实例,这听起来很像您想要做的事情 - 从 TypeListener 中读取您创建的注释的参数并不困难。但是,这不适用于@Inject注释或构造函数,因此,如果您尝试完全在幕后进行注入,则可能会遇到麻烦。

另一个选项要简单得多:只需使用工厂并传入新构造的类。

public MyClass {
private final String name;
@Inject
public MyClass(NameInjector nameInjector) {
this.name = nameInjector.get(this, "foo", true);
}
}

对于普通的Guice注入,您无法访问正在注入某些东西的类的名称。如果您确实需要这样做,则需要使用自定义注入。

通过使用自定义TypeListener,可以侦听注入事件并知道正在注入的类。听到注入事件时,您可以注册一个自定义MembersInjector,Guice 将在完成自己的注入调用该。此MembersInjector可以访问类的完全构造实例,因此它可以反映字段并检查注释。但是,它显然不能注入构造函数参数,因为对象已经创建。

简而言之,无法对构造函数参数进行自定义注入。但是你描述的想法很有可能用于现场注入!

怎么做

首先,您需要注册一个TypeListener(此代码基于链接的Guice wiki页面):

public class NamedStringListener implements TypeListener {
public <T> void hear(TypeLiteral<T> typeLiteral, TypeEncounter<T> typeEncounter) {
Class<?> clazz = typeLiteral.getRawType();
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
if (field.getType() == String.class &&
field.isAnnotationPresent(NamedInjectable.class)) {
Annotation annotation = field.getAnnotation(NamedInjectable.class);
// How you create and configure this provider is up to you.
Provider<String> provider = new MyStringProvider(clazz, annotation);
typeEncounter.register(new MyMembersInjector<T>(field, provider));
}
}
clazz = clazz.getSuperclass();
}
}
}

然后,在MyMembersInjector<T>里面:

public class MyMembersInjector<T> implements MembersInjector<T> {
final Field field;
final Provider<String> provider;
NamedMembersInjector(Provider<String> provider) {
this.field = field;
this.provider = provider;
this.field.setAccessible(true);
}
public void injectMembers(T t) {
field.set(t, provider.get());
}
}

我把MyStringProvider的实施留给你。

请参阅 Guice CustomInjections wiki 页面了解更多信息。

相关内容

最新更新