【问题标题】:How to inject indexed (and class specific) strings using Guice如何使用 Guice 注入索引(和类特定)字符串
【发布时间】:2016-05-27 21:55:37
【问题描述】:

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

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

    boolean indexed() default true;
}

其中 name 是字符串的新名称库(默认仅为类的名称),indexed 说明每次注入新字符串时是否应递增名称。
例如

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

并且name 参数应该被赋予一个值,例如“ 我考虑过使用 Provider BindingsAssistedInject 但我可以完成它。失败的一个主要原因是不知何故获得了类的名称。

你还有什么想法吗?

【问题讨论】:

    标签: java dependency-injection guice


    【解决方案1】:

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

    除了标准的@Inject 驱动注入之外,Guice 还包括用于自定义注入的钩子。这使 Guice 能够托管具有自己的注入语义或注释的其他框架。大多数开发人员不会直接使用自定义注入;但他们可能会在扩展和第三方库中看到它们的用途。每个自定义注入都需要一个类型侦听器、一个注入侦听器,并分别注册。

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

    另一种选择要简单得多:只需使用工厂并传入新构造的类。

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

    【讨论】:

    • 我发现工厂注入更适合我的目的。虽然这看起来有点矫枉过正 =)
    【解决方案2】:

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

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

    简而言之,没有办法对构造函数参数进行自定义注入。但是你描述的想法对于现场注入是很有可能的!

    怎么做

    首先,您需要注册一个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&lt;T&gt;

    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 wiki 页面了解更多信息。

    【讨论】:

    • 谢谢。我已经尝试过CustomInjections。据我了解,由于不同的原因,不鼓励字段注入(将 final 字段排除在外......)
    猜你喜欢
    • 2015-08-28
    • 2022-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-17
    • 2017-11-19
    • 2019-01-11
    • 1970-01-01
    相关资源
    最近更新 更多