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

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

问题描述

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

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

AutoConfig注释

AutoConfig 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;

我想要两个配置,一个是默认/ 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)ConfigLoader<?> jsonConfig 将完全返回 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< Key,Provider> ,带有花哨的包装。坏消息是,这使得所有T的绑定 Foo< T> 之类的变量绑定无法简洁地表达,包括你的bind 所有T的@Annotation(T)Foo 。好消息是你还有两个选择。

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为您这样做),如果您绑定注释​​实例,Guice将使用其等于方法比较注释而不是注释 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 。请注意,等于 hashCode 的实现必须与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);
}

的定义等于取决于你,这意味着你可以(并且应该)绑定 @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.

使用上面链接的wiki文章作为您的模板,但至少您需要:

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 -annotated字段的注入类型,如果它们有任何匹配的方法,则将这些匹配方法绑定到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天全站免登陆