如何使用 Guice 注入索引(和 class 特定)字符串

How to inject indexed (and class specific) strings using Guice

我最近一直在使用 Google Guice 进行黑客攻击,我想出了一个想法,根据声明的 class 将 String 注入构造函数in 和注释中定义的其他几个参数。例如: 如果我定义一个新的限定符注释 @NamedInjectable 供 Guice 使用:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Qualifier
public @interface NamedInjectable
{
    String name() default "";

    boolean indexed() default true;
}

其中 name 是字符串的新名称基础(默认仅为 class' 名称),并且 indexed 说明每次新字符串出现时名称是否应该递增正在注射。
例如

public MyClass {
    @Inject
    public MyClass(@NamedInjectable(name = "foo", indexed = true) String name) {
        // some code
    }
}

name param应该被赋予一个值,例如“ 我考虑过使用 Provider BindingsAssistedInject 但我可以完成它。失败的一个主要原因是以某种方式获取 class.

的名称

你还有别的想法吗?

没有基于名称自定义标准 Guice 绑定的内置方法。如果你想单独使用 Guice,你可能需要 Custom Injections.

In addition to the standard @Inject-driven injections, Guice includes hooks for custom injections. This enables Guice to host other frameworks that have their own injection semantics or annotations. Most developers won't use custom injections directly; but they may see their use in extensions and third-party libraries. Each custom injection requires a type listener, an injection listener, and registration of each.

Guice 的自定义注入文档示例演示了一个使用注入类型自定义的记录器实例 class,这听起来很像您想要做的事情——阅读注释的参数不再困难您从 TypeListener 中创建。但是,这不能直接与 @Inject 注释或构造函数一起使用,因此如果您试图让注入完全在幕后发生,您可能会遇到麻烦。

另一种选择更简单:只需使用工厂并传入新建的 class。

public MyClass {
    private final String name;

    @Inject
    public MyClass(NameInjector nameInjector) {
        this.name = nameInjector.get(this, "foo", true);
    }
}

对于普通的 Guice 注入,您无法访问正在注入内容的 class 的名称。如果您确实需要这样做,则需要使用自定义注入。

通过使用自定义 TypeListener,您可以侦听注入事件并了解正在注入的 class。在听到注入事件时,您可以注册一个自定义 MembersInjector,Guice 将在 完成自己的注入后 调用它。此 MembersInjector 可以访问 class 的完整构造实例,因此它可以反映字段并检查注释。但是,它显然不能注入构造函数参数,因为对象已经创建了。

简而言之,构造函数参数的自定义注入是没办法的。但是你描述的这个idea对于field injection是很有可能的!

怎么做

首先,您需要注册一个 TypeListener(此代码基于链接的 Guice wiki 页面):

public class NamedStringListener implements TypeListener {
    public <T> void hear(TypeLiteral<T> typeLiteral, TypeEncounter<T> typeEncounter) {
        Class<?> clazz = typeLiteral.getRawType();
        while (clazz != null) {
             for (Field field : clazz.getDeclaredFields()) {
             if (field.getType() == String.class &&
                 field.isAnnotationPresent(NamedInjectable.class)) {
                     Annotation annotation = field.getAnnotation(NamedInjectable.class);

                     // How you create and configure this provider is up to you.
                     Provider<String> provider = new MyStringProvider(clazz, annotation);
                     typeEncounter.register(new MyMembersInjector<T>(field, provider));
                 }
             }
         clazz = clazz.getSuperclass();
         }
     }
}

然后,在MyMembersInjector<T>里面:

public class MyMembersInjector<T> implements MembersInjector<T> {
    final Field field;
    final Provider<String> provider;

    NamedMembersInjector(Provider<String> provider) {
        this.field = field;
        this.provider = provider;
        this.field.setAccessible(true);
    }

    public void injectMembers(T t) {
         field.set(t, provider.get());
    }
}

我将 MyStringProvider 的实施留给您。

有关更多信息,请参阅 Guice CustomInjections 维基页面。