Guice基于注释值注入

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

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; 

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

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)或在构造函数中将注释(或其值提供给提供者)。 目前我只使用构造函数来注入我想在新生成的配置实例上设置的configFile值。

如果你知道@AutoConfig(provider = JsonConfigProvider) ConfigLoader jsonConfig将准确返回jsonConfigProvider.get()的结果,而JsonConfigProvider显然有一个公共无参数构造函数可供newInstance工作,你为什么不呢?首先要求一个JsonConfigProvider

从根本上说,Guice只是一个Map带有花哨的包装。 坏消息是,这使得变量绑定如“为所有T绑定Foo ”无法简洁地表达,并且包括你的“绑定@Annotation(T) Foo for all T”。 好消息是你还有两种选择。

单独绑定每个提供商

虽然您无法在提供期间检查注释(或告诉Guice为您执行此操作),但如果您绑定注释实例而不是注释 ,Guice将使用其equals方法比较注释(与Names.named("some-name") )。 这意味着您可以将ConfigLoader与模块中的每个预期注释绑定。 当然,这也意味着您必须在配置时获得可用的ConfigLoader提供程序列表,但如果您将它们用作注释参数,则它们必须是编译时常量。

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

要做到这一点,你将不得不编写注释的实现,就像Guice用NamedImpl做的NamedImpl 。 请注意, equalshashCode的实现必须与Java在java.lang.Annotation提供的实现相匹配。 那么这只是(冗余)绑定的问题:

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

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

使用自定义注射

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

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

  1. 在Binder或AbstractModule上使用bindListener来匹配需要此自定义注入的类型。
  2. 在您绑定的TypeListener中,搜索@AutoConfig -annotated字段的注入类型,如果它们具有任何匹配方法,则将这些匹配方法绑定到MembersInjector或InjectionListener。 您可能希望在此处取出注释实例中的类文字,并将Field和Class作为构造函数参数传递给MembersInjector / InjectionListener。
  3. 在您编写的MembersInjector或InjectionListener中,实例化提供程序并将字段设置为提供程序提供的实例。

这是一个非常强大的function,它将进一步允许您 – 例如 – 根据您注入的实例或基于字段名称自动提供配置。 但是,请仔细使用并大量记录,因为Guice提供除@Inject以外的注释可能会对您的同事违反直觉。 还要记住,这对构造函数注入不起作用,因此从字段注入到构造函数注入的重构将导致Guice抱怨它缺少实例化类所需的绑定。

我有类似的问题。 我想使用一个接收枚举参数的自定义注释来选择实现。 经过大量的研究,调试和测试,我得出了以下解决方案:

 //enum to define authentication types public enum AuthType { Ldap, Saml } //custom annotation to be used in injection @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) @BindingAnnotation public @interface Auth { AuthType value(); } //defintion of authenticator public interface Authenticator { public void doSomehting(); } //Authenticator implementations public class LdapAuthenticator implements Authenticator { @Override public void doSomehting() { // doing ldap stuff } } public class SamlAuthenticator implements Authenticator { @Override public void doSomehting() { // doing saml stuff } } public class MyModule extends AbstractModule { // annotate fields to bind to implementations private @Auth(AuthType.Ldap) Authenticator ldap; private @Auth(AuthType.Saml) Authenticator saml; @Override protected void configure() { //bind the implementation to the annotation from field bindAnnotated("ldap", LdapAuthenticator.class); bindAnnotated("saml", SamlAuthenticator.class); } private void bindAnnotated(String fieldName, Class implementation) { try { //get the annotation from fields, then bind it to implementation Annotation ann = MyModule.class.getDeclaredField(fieldName).getAnnotation(Auth.class); bind(Authenticator.class).annotatedWith(ann).to(implementation); } catch (NoSuchFieldException | SecurityException e) { throw new RuntimeException(e); } } } //usage: add @Auth() to the dependency public class ClientClass { private Authenticator authenticator; @Inject public ClientClass(@Auth(AuthType.Ldap) Authenticator authenticator) { this.authenticator = authenticator; } } 

查看Binder的文档

我测试了Jeff Bowman解决方案,但它显然只能与提供商绑定