[英]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
。 請注意, equals
和hashCode
的實現必須與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文章中的示例作為模板,但至少您需要:
bindListener
來匹配需要此自定義注入的類型。 @AutoConfig
-annotated字段的注入類型,如果它們具有任何匹配方法,則將這些匹配方法綁定到MembersInjector或InjectionListener。 您可能希望在此處取出注釋實例中的類文字,並將Field和Class作為構造函數參數傳遞給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.