我想使用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;
}
这是我的注释,它允许配置应该用于带注释字段的配置类型。
USECASE:
@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值。
答案 0 :(得分:3)
如果您知道@AutoConfig(provider = JsonConfigProvider) ConfigLoader<?> jsonConfig
将完全返回jsonConfigProvider.get()
的结果,并且JsonConfigProvider显然有newInstance
的公共无参数构造函数可以工作,那为什么不会你刚才首先要求JsonConfigProvider
?
从根本上讲,Guice只是一个Map<Key, Provider>
的花式包装。坏消息是,这使得所有T&#34的变量绑定类似于&#34;绑定Foo<T>
。不可能简洁地表达,包括你的所有T&#34;绑定@Annotation(T) Foo
。好消息是你还有两种选择。
虽然您无法在提供期间检查注释(或告诉Guice为您执行此操作),但如果您绑定注释实例,Guice将使用其equals
方法比较注释而不是注释 class (与Names.named("some-name")
的方式)。这意味着您可以将ConfigLoader<?>
与模块中的每个预期注释绑定。当然,这也意味着您必须在配置时拥有可用的ConfigLoader提供程序列表,但如果您将它们用作注释参数,则它们必须是编译时常量。
此解决方案也适用于构造函数注入,但对于字段,您需要@Inject
和@AutoConfig(...)
,并且AutoConfig需要保留其@BindingAnnotation
元注释。
要做到这一点,你必须编写注释的实现,就像Guice用NamedImpl
做的那样。请注意,equals
和hashCode
的实现必须与java.lang.Annotation
中Java提供的实现相匹配。然后它只是(冗余地)绑定的问题:
for(Class<ConfigLoader<?>> clazz : loaders) {
bind(ConfigLoader.class).annotatedWith(new AutoConfigImpl(clazz))
.toProvider(clazz);
}
equals
的定义取决于您,这意味着您可以(并且应该)绑定@AutoConfig(ConfigEnum.JSON)
并将Guice绑定保留在模块中,而不是在整个代码库中指定所请求的实现。< / p>
您还可以使用custom injections在注入的类型中搜索@AutoConfig
等自定义注释。此时,您使用Guice作为平台来解释@AutoConfig
而不是 @Inject
,这意味着构造函数注入赢了&但是你可以根据注入的实例,字段名称,字段注释,注释参数或其任何组合来控制注入。如果选择此样式,则可以从AutoConfig中删除@BindingAnnotation
。
使用the wiki article linked above中的示例作为模板,但至少您需要:
bindListener
来匹配需要此自定义注入的类型。@AutoConfig
- 注释字段的注入类型,如果它们具有任何匹配方法,则将这些匹配方法绑定到MembersInjector或InjectionListener。您可能希望在此处取出注释实例中的类文字,并将Field和Class作为构造函数参数传递给MembersInjector / InjectionListener。这是一个非常强大的功能,它还可以让您 - 例如 - 根据您注入的实例或根据字段名称自动提供配置。但是,请仔细使用并大量记录,因为Guice提供的注释可能与@Inject
以外的注释相反。还要记住,这不适用于构造函数注入,因此从字段注入到构造函数注入的重构将导致Guice抱怨它缺少实例化类所需的绑定。
答案 1 :(得分:1)
我有类似的问题。我想使用一个自定义注释来接收枚举参数来选择实现。经过大量的研究,调试和测试,我得出了以下解决方案:
//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解决方案,但它显然只能与提供商绑定