我想使用 goolge/guice 根据我提供的带有注释的类注入(inject)一个值。
AutoConfig 注释
@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.PARAMETER, ElementType.FIELD })
public @interface AutoConfig {
// default null not possible
Class<? extends Provider<? extends ConfigLoader<?>>> provider() default XMLAutoConfigProvider.class;
}
这是我的注释,它允许配置应该用于注释字段的配置类型。
用例:
@AutoConfig()
ConfigLoader<?> defaultConfig;
@AutoConfig(provider = JsonConfigProvider)
ConfigLoader<?> jsonConfig;
我想要两个配置,一个是 default/xml,一个是 json。它们可能永远不会同时出现在同一个类(class)中。但是我不知道什么时候使用一个或另一个。我将这种方法与类一起使用,因为它们是由一些依赖项/库提供的,并且此注释将用于某些(可插入的)子模块。
MyGuiceModule
public class MyGuiceModule extends AbstractModule {
@Override
protected void configure() {
bind(new TypeLiteral<ConfigLoader<?>>() {})
.annotatedWith(AutoConfig.class)
.toProvider(autoConfig.provider());
}
}
这是关键部分,我无法想象如何实现它。
所以基本上我只想使用注释中指定的提供程序类。 也没有必要在这里使用提供者类。因为 autoConfig.provider().newInstance() 基本上是我所需要的。 (我需要在新实例上使用 setter,但这就是我想在这个地方做的所有事情)
总而言之,我真正想做的就是使用 get(AutoConfig autoConfig) 或在构造函数中将注释(或其值推送给提供者)。 目前我只使用构造函数注入(inject)我想在新生成的配置实例上设置的 configFile 值。
最佳答案
如果你知道@AutoConfig(provider = JsonConfigProvider) ConfigLoader<?> jsonConfig
将准确返回给您 jsonConfigProvider.get()
的结果,并且 JsonConfigProvider 显然有一个公共(public)无参数构造函数 newInstance
工作,你为什么不直接要一个 JsonConfigProvider
首先?
从根本上说,Guice 只是一个 Map<Key, Provider>
精美的包装。坏消息是,这使得像“为所有 T 绑定(bind) Foo<T>
”这样的变量绑定(bind)无法简洁地表达,这包括您的“为所有 T 绑定(bind) @Annotation(T) Foo
”。好消息是您还有两个选择。
分别绑定(bind)每个提供者
虽然您无法在提供期间检查注释(或告诉 Guice 为您这样做),但 Guice 将使用它们的 equals
来比较注释。方法,如果你绑定(bind)一个注解 instance 而不是一个注解 class (你会用 Names.named("some-name")
的方式)。这意味着您可以绑定(bind) ConfigLoader<?>
与模块中的每个预期注释。当然,这也意味着您必须在配置时有一个可用的 ConfigLoader Provider 列表,但如果您将它们用作注释参数,它们无论如何都必须是编译时常量。
此解决方案也适用于构造函数注入(inject),但对于字段,您需要 @Inject
和 @AutoConfig(...)
, AutoConfig 将需要保留其 @BindingAnnotation
元注释。
为此,您将不得不编写注释的实现,就像 Guice 处理 NamedImpl
的方式一样。 .请注意 equals
的实现和 hashCode
必须与 Java 在 java.lang.Annotation
中提供的匹配.那么这只是一个(冗余)绑定(bind)的问题:
for(Class<ConfigLoader<?>> clazz : loaders) {
bind(ConfigLoader.class).annotatedWith(new AutoConfigImpl(clazz))
.toProvider(clazz);
}
equals
的定义由您决定,这意味着您可以(并且应该)绑定(bind) @AutoConfig(ConfigEnum.JSON)
并在您的模块中保留 Guice 绑定(bind),而不是在整个代码库中指定您请求的实现。
使用自定义注入(inject)
您还可以使用 custom injections在你的注入(inject)类型中搜索自定义注释,如 @AutoConfig
.此时,您将使用 Guice 作为平台 来解释 @AutoConfig
而不是 @Inject
,这意味着构造函数注入(inject)将不起作用,但您可以根据注入(inject)的实例、字段名称、字段注释、注释参数或其任意组合来控制注入(inject)。如果选择这种款式,可以滴@BindingAnnotation
来自 AutoConfig。
使用 the wiki article linked above 中的示例作为您的模板,但您至少需要:
- 使用
bindListener
在 Binder 或 AbstractModule 上匹配需要此自定义注入(inject)的类型。 - 在您绑定(bind)的 TypeListener 中,搜索
@AutoConfig
的注入(inject)类型- 带注释的字段,如果它们有任何匹配的方法,则将这些匹配的方法绑定(bind)到 MembersInjector 或 InjectionListener。您可能希望从此处的注释实例中提取类文字,并将 Field 和 Class 作为构造函数参数传递给 MembersInjector/InjectionListener。 - 在您编写的 MembersInjector 或 InjectionListener 中,实例化提供者并将字段设置为提供者提供的实例。
这是一个非常强大的功能,它进一步允许您——例如——根据您要注入(inject)的实例或根据字段名称自动提供配置。但是,请小心使用它并大量记录它,因为 Guice 提供的注释不是 @Inject
可能会让您的同事违反直觉。 .还要记住,这不适用于构造函数注入(inject),因此从字段注入(inject)重构为构造函数注入(inject)将导致 Guice 提示它缺少实例化类所需的绑定(bind)。
关于java - Guice 注入(inject)基于注解值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28549549/