【问题标题】:How do you make dynamic bindings in Guice that require an injected Instance?如何在 Guice 中进行需要注入实例的动态绑定?
【发布时间】:2012-11-06 01:08:07
【问题描述】:

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

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

  1. configure() 方法中的绑定。 该方法没有注入,无法获取基础配置。

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

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

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

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

【问题讨论】:

    标签: java dynamic configuration guice


    【解决方案1】:

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

    1. @Named 属性绑定到单个Properties 实例的内容非常有用,有一个Names.bindProperties(...) 方法可以自动为您执行此操作。唯一的诀窍是在运行 configure() 时您需要拥有 Properties 实例。

      如果它们都同时可用,则不必担心在一个模块中绑定属性而在另一个模块中绑定应用程序。只要它们都进入同一个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. 您永远不需要将Binder(或其他任何东西)注入到模块中。

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

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

    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);
    

    【讨论】:

    • 另一个好答案,谢谢!我不知道 Names.bindProperties() 方法,我必须为后者保存它。我昨晚尝试了您在 2 中提出的解决方案,它大部分都有效,虽然不是那么干净,但我会喜欢它。我基本上得出的结论是,我真正想做的事情超出了 Guice 的设计。我不拥有我正在部署的框架,并且我不相信子注入器将很快成为可用的功能,因此我将重新设计我们的配置有点不同。不过感谢您的及时答复!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-16
    • 1970-01-01
    相关资源
    最近更新 更多