基于注解值的Guice注入 [英] Guice inject based on annotation value

查看:43
本文介绍了基于注解值的Guice注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用 goolge/guice 根据我提供的注释类注入一个值.

I would like to use goolge/guice inject a value based on a class i provide with the annotation.

自动配置注解

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

这是我的注释,它允许配置配置类型,应该用于带注释的字段.

This is my annotation which allows configuring the type of config, that should be used for the annotated fields.

用例:

@AutoConfig()
ConfigLoader<?> defaultConfig;

@AutoConfig(provider = JsonConfigProvider)
ConfigLoader<?> jsonConfig;

我想要两个配置,一个是 default/xml,一个是 json.它们可能永远不会同时出现在同一个班级中.但我不知道何时使用一个或另一个.我在类中使用了这种方法,因为它们由某些依赖项/库提供,并且此注释将用于某些(可插入的)子模块.

I want to have two configs, one default/xml one and a json one. They will probably never occur in the same class at the same time. But i don't know when the one or the other is used. I used the approach with a class because they are provided by some dependencies/libs and this annotation will be used for some (plugable) submodules.

MyGuiceModule

MyGuiceModule

public class MyGuiceModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(new TypeLiteral<ConfigLoader<?>>() {})
            .annotatedWith(AutoConfig.class)
            .toProvider(autoConfig.provider());
    }
}

这是关键部分,我无法想象如何实现它.

This the critical part, i just cannot imagine how to implement it.

所以基本上我只想使用注释中指定的提供程序类.这里也没有必要使用提供者类.因为 autoConfig.provider().newInstance() 基本上就是我所需要的.(我需要在新实例上使用 setter,但这就是我想在这个地方做的)

So basically i just want to use the provider class specified in the annotation. Its not necessary to use the provider class here too. Because autoConfig.provider().newInstance() is basically all i need. (I need to use a setter on the new instance but thats all i want to do at this place)

总而言之,我真正想做的就是使用 get(AutoConfig autoConfig) 或在构造函数中推送注释(或其值到提供者).目前我只使用构造函数来注入我想在新生成的配置实例上设置的 configFile 值.

To sum it up all i really want to do is push the annotation (or its values to the provider) either using the get(AutoConfig autoConfig) or in the constructor. Currently i only use the constructor to inject the configFile value i want to set on the newly generated config instance.

推荐答案

如果你知道 @AutoConfig(provider = JsonConfigProvider) ConfigLoaderjsonConfig 将准确地返回 jsonConfigProvider.get() 的结果,而 JsonConfigProvider 显然有一个公共的无参数构造函数供 newInstance 工作,为什么不你不是一开始就要求一个 JsonConfigProvider 吗?

If you know that @AutoConfig(provider = JsonConfigProvider) ConfigLoader<?> jsonConfig is going to return you exactly the results of jsonConfigProvider.get(), and JsonConfigProvider obviously has a public parameterless constructor for newInstance to work, why wouldn't you just ask for a JsonConfigProvider in the first place?

从根本上说,Guice 只是一个带有精美包装的 Map.坏消息是,这使得像bind Foo<T> for all T"这样的变量绑定无法简明地表达,其中包括您的bind @Annotation(T) Foo 为所有 T".好消息是您还有两个选择.

Fundamentally Guice is just a Map<Key, Provider> with fancy wrapping. The bad news is that this makes variable bindings like "bind Foo<T> for all T" impossible to express concisely, and that includes your "bind @Annotation(T) Foo for all T". The good news is that you still have two options.

尽管您无法在提供期间检查注解(或告诉 Guice 为您检查),但如果您绑定注解实例equals 方法比较注解> 而不是注释 class(您使用 Names.named("some-name") 的方式).这意味着您可以将 ConfigLoader 与模块中的每个预期注释绑定.当然,这也意味着您必须有一个在配置时可用的可能的 ConfigLoader 提供程序列表,但如果您将它们用作注释参数,它们无论如何都必须是编译时常量.

Though you can't inspect annotations during provision (or tell Guice to do so for you), Guice will compare annotations using their equals methods if you bind an annotation instance rather than an annotation class (the way you would with Names.named("some-name")). This means that you can bind a ConfigLoader<?> with each expected annotation in a Module. Of course, this also means you'll have to have a list of possible ConfigLoader Providers available at configuration time, but they have to be compile-time constants anyway if you're using them as annotation parameters.

此解决方案也适用于构造函数注入,但对于字段,您需要 @Inject@AutoConfig(...),并且 AutoConfig 将需要保留它的 @BindingAnnotation 元注释.

This solution works with constructor injection as well, but for fields you'll need both @Inject and @AutoConfig(...), and AutoConfig will need to keep its @BindingAnnotation meta-annotation.

为此,您必须编写注释的实现,就像 Guice 对 NamedImpl.请注意,equalshashCode 的实现必须与 Java 在 java.lang.Annotation 中提供的实现相匹配.那么这只是一个(冗余)绑定的问题:

To do this, you're going to have to write an implementation of your annotation, the way Guice does with NamedImpl. Note that the implementations of equals and hashCode must match the ones Java provides in java.lang.Annotation. Then it's just a matter of (redundantly) binding like this:

for(Class<ConfigLoader<?>> clazz : loaders) {
  bind(ConfigLoader.class).annotatedWith(new AutoConfigImpl(clazz))
      .toProvider(clazz);
}

equals 的定义取决于您,这意味着您可以(并且应该)绑定 @AutoConfig(ConfigEnum.JSON) 并将 Guice 绑定保留在您的模块中而不是在整个代码库中指定您请求的实现.

The definition of equals is up to you, which means you can (and should) bind @AutoConfig(ConfigEnum.JSON) and keep the Guice bindings in your modules rather than specifying your requested implementation all over your codebase.

您还可以使用 自定义注入 来搜索您注入的类型以查找自定义注释,例如 <代码>@AutoConfig.此时,您将使用 Guice 作为平台来解释 @AutoConfig 而不是 @Inject,这意味着构造函数注入不起作用,但您可以根据注入的实例、字段名称、字段注释、注释参数或它们的任意组合来控制注入.如果选择此样式,则可以从 AutoConfig 中删除 @BindingAnnotation.

You can also use custom injections to search your injected types for custom annotations like @AutoConfig. At this point, you'd be using Guice as a platform to interpret @AutoConfig instead of @Inject, which means that constructor injection won't work but that you can control your injection based on the injected instance, field name, field annotation, annotation parameters, or any combination thereof. If you choose this style, you can drop @BindingAnnotation from AutoConfig.

使用上面链接的维基文章中的示例作为模板,但至少你需要:

Use the example in the wiki article linked above as your template, but at minimum you'll need to:

  1. 在 Binder 或 AbstractModule 上使用 bindListener 来匹配需要此自定义注入的类型.
  2. 在您绑定的 TypeListener 中,搜索 @AutoConfig 注释字段的注入类型,如果它们有任何匹配的方法,则将这些匹配的方法绑定到 MembersInjector 或 InjectionListener.您可能希望在此处从注释实例中提取类文字,并将 Field 和 Class 作为构造函数参数传递给 MembersInjector/InjectionListener.
  3. 在您编写的 MembersInjector 或 InjectionListener 中,实例化提供程序并将字段设置为提供程序提供的实例.
  1. Use bindListener on Binder or AbstractModule to match types that need this custom injection.
  2. In the TypeListener you bind, search injected types for @AutoConfig-annotated fields, and if they have any matching methods then bind those matching methods to a MembersInjector or InjectionListener. You'll probably want to tease the class literal out of the annotation instance here, and pass in the Field and Class as constructor arguments to the MembersInjector/InjectionListener.
  3. In the MembersInjector or InjectionListener you write, instantiate the provider and set the field to the instance the provider provides.

这是一个非常强大的功能,它将进一步允许您——例如——根据您注入的实例或基于字段的名称自动提供配置.但是,请谨慎使用它并大量记录它,因为对于您的同事来说,Guice 提供的注释可能是 @Inject 以外的注释.还要记住,这不适用于构造函数注入,因此从字段注入重构为构造函数注入会导致 Guice 抱怨它缺少实例化类所需的绑定.

This is a very powerful feature, which would futher allow you to--for instance--automatically provide the configuration based on which instance you're injecting into or based on the name of the field. However, use it carefully and document it heavily, because it may be counter-intuitive to your coworkers that Guice is providing for an annotation other than @Inject. Also bear in mind that this won't work for constructor injection, so refactoring from field injection to constructor injection will cause Guice to complain that it's missing a required binding to instantiate the class.

这篇关于基于注解值的Guice注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆