9

我想创建一个将实例动态绑定到命名注释的模块。用例是我想自动将我的配置中的值与属性文件中的键绑定为@Named 值。

但是配置绑定在不同的模块中,所以我需要注入配置。我看过的解决方案是:

  1. 在 configure() 方法中绑定。此方法未注入,我无法获取基本配置。

  2. 使用提供者/@Provides。提供者只绑定一个实例。

  3. 使用 MultiBinder。我的用例与这个扩展提供的有点不同。多重绑定允许您分别绑定多个实例,然后将它们作为更复杂的包含类型的 Collection 注入。我想分别绑定每个实例,并让它们通过唯一可识别的方式注入。

  4. 使用 childInjector。不幸的是,如果不对现有代码进行一些广泛的修改,这是不可能的。这个答案很好地描述了如何以这种方式解决这个问题。

  5. 以某种方式注入粘合剂。(我开始变得有点 hackier)Guice 允许注入 Injector 以供以后使用,我尝试通过 @Provides 方法将 Binder 注入到模块中,然后直接使用 binder 在方法中进行多个绑定。Guice 不会注入粘合剂。

4

1 回答 1

9

请记住,在任何注入发生之前,所有configure方法都会配置所有绑定。也就是说,有几件事:Injector

  1. 将属性绑定@Named到单个Properties实例的内容非常有用,有一种Names.bindProperties(...)方法可以自动为您完成。唯一的技巧是您需要Properties在运行时拥有实例configure()

    如果它们同时可用,则不必担心将属性绑定到一个模块中并将应用程序绑定到另一个模块中。只要它们都进入同一个Injector,Guice就会将它们全部组合起来,让它们满足彼此的依赖关系。

  2. 提供者可以返回不同的实例,并且通常会这样做——但你是对的,它不会帮助你区分键。如果直接注入 Properties 实例太难看,可以考虑创建一个轻量级工厂:

    public class ConfigOracle {
      @Inject private Properties properties;
    
      public String getAsString(String key) { ... }
      public int getAsInt(String key) { ... }
    }
    
    public class SomeConfigUser {
      @Inject private ConfigOracle configOracle;
    
      public void doStuff() {
        doStuffBasedOn(configOracle.getAsString("my.properties.key"));
      }
    }
    
  3. 您永远不需要将 a Binder(或其他任何东西)注入到模块中。

    • 如果实现Modulebinder将是 的参数configure()。如果您AbstractModule按照应有的方式进行扩展,只需调用该binder()方法即可。
    • 如果需要,您可以通过构造函数参数将依赖项传递给模块,这(就我而言)是模块应该改变它们创建的绑定的唯一方法。
    • 没有理由你不能通过 Injector 创建一个 Module,但你必须首先拥有一个 Injector,听起来你只是想逃避只有一个。
    • 如果您需要注入器中的其他实例,您始终可以Provider使用字段/方法/构造函数编写实现@Inject,甚至可以在@Provides方法中接收参数(将自动填充依赖项)。

总的来说,我仍然喜欢子注入器方法(感谢链接和对我之前回答的赞美!),它最适合您的“基于注入实例的动态绑定”描述,并且实际上就是这么简单:

class PropertiesModule extends AbstractModule {
  Properties properties;

  PropertiesModule(Properties properties) {
    this.properties = properties;
  }

  @Override public void configure() {
    Names.bindProperties(binder(), properties);
  }
}

Injector oldInjector = Guice.createInjector(allYourOtherModules);
Module myModule = new PropertiesModule(oldInjector.get(Properties.class));
Injector injector = oldInjector.createChildInjector(myModule);
于 2012-11-06T04:18:41.873 回答