簡體   English   中英

Guice基於注釋值注入

[英]Guice inject based on annotation value

我想使用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<Key, Provider>帶有花哨的包裝。 壞消息是,這使得像“為所有T綁定Foo<T> ”這樣的變量綁定不可能簡潔地表達,並且包括你的“為所有T綁定@Annotation(T) Foo ”。 好消息是你還有兩個選擇。

單獨綁定每個提供商

雖然您無法在提供期間檢查注釋(或告訴Guice為您執行此操作),但如果您綁定注釋實例而不是注釋 ,Guice將使用其equals方法比較注釋(與Names.named("some-name") )。 這意味着您可以將ConfigLoader<?>與模塊中的每個預期注釋綁定。 當然,這也意味着您必須在配置時獲得可用的ConfigLoader提供程序列表,但如果您將它們用作注釋參數,則它們必須是編譯時常量。

此解決方案也適用於構造函數注入,但對於字段,您需要@Inject@AutoConfig(...) ,並且AutoConfig將需要保留其@BindingAnnotation元注釋。

要做到這一點,你將不得不編寫注釋的實現,就像Guice用NamedImpl做的NamedImpl 請注意, equalshashCode的實現必須與Java在java.lang.Annotation提供的實現相匹配。 那么這只是(冗余)綁定的問題:

for(Class<ConfigLoader<?>> 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中,實例化提供程序並將字段設置為提供程序提供的實例。

這是一個非常強大的功能,這將進一步允許您 - 例如 - 根據您注入的實例或基於字段名稱自動提供配置。 但是,請仔細使用並大量記錄,因為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<? extends Authenticator> 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(<AuthType>) to the dependency

public class ClientClass {

    private Authenticator authenticator;

    @Inject
    public ClientClass(@Auth(AuthType.Ldap) Authenticator authenticator) {
        this.authenticator = authenticator;
    }
}

查看Binder的文檔

我測試了Jeff Bowman解決方案,但它顯然只能與提供商綁定

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM