Java 是否可以在Guice中使用Multibinder根据应用程序配置构造动态集?

Java 是否可以在Guice中使用Multibinder根据应用程序配置构造动态集?,java,configuration,guice,multibinding,Java,Configuration,Guice,Multibinding,我有一个应用程序,它依赖于属性配置来确定是否混合各种组件。 例如,配置具有布尔标志,如“componentX.enabled”等,用于确定这些组件是否应处于活动状态 目前,我在我的提供程序方法中使用以下标志: @Provides @Singleton @Nullable public ComponentX provideComponentX(Properties props) { if (props.isComponentXEnabled()) { return new C

我有一个应用程序,它依赖于属性配置来确定是否混合各种组件。 例如,配置具有布尔标志,如“componentX.enabled”等,用于确定这些组件是否应处于活动状态

目前,我在我的提供程序方法中使用以下标志:

@Provides
@Singleton
@Nullable
public ComponentX provideComponentX(Properties props) {
    if (props.isComponentXEnabled()) {
       return new ComponentX();
    } else {
       return null;
    }
}

@Provides
@Singleton
public Set<Component> provideComponentSet(
   @Nullable ComponentX compX,
   ComponentY compY,
   ComponentZ compZ
) {
   Set<Component> comps = new HashSet<>();
   if (compX != null) {
      comps.add(compX);
   }

   comps.add(compY);
   comps.add(compZ);
   return comps;
}
@提供
@独生子女
@可空
公共组件X provideComponentX(属性道具){
if(props.isComponentXEnabled()){
返回新组件x();
}否则{
返回null;
}
}
@提供
@独生子女
公共集ProviderComponentSet(
@可为空的组件x compX,
成分公司,
组件
) {
Set comps=new HashSet();
如果(compX!=null){
公司增加(公司);
}
公司增加(公司);
公司地址(公司地址);
返回comps;
}
这种方法似乎有点笨拙(它依赖于可能的注入null)——但是有更好的方法吗

我能想到的唯一其他方法是使用父注入器将应用程序属性获取到我的模块中,然后使用set Multibinder

然后将创建子注入器与新模块一起使用,以完成引导过程

public class Module extends AbstractModule {
    Properties props;
    public Module(Properties props) {
      this.props = props;
    }

    public void configure() {
        Multibinder<Component> compBinder = Multibinder.newSetBinder(binder(), Component.class);

        if (props.isComponentXEnabled()) {
            compBinder.addBinding().to(ComponentX.class);
        }
        compBinder.addBinding().to(ComponentY.class);
        compBinder.addBinding().to(ComponentZ.class);
    }

}
公共类模块扩展了AbstractModule{
属性道具;
公共模块(属性道具){
this.props=props;
}
public void configure(){
Multibinder compBinder=Multibinder.newSetBinder(binder(),Component.class);
if(props.isComponentXEnabled()){
compBinder.addBinding().to(ComponentX.class);
}
compBinder.addBinding().to(ComponentY.class);
compBinder.addBinding().to(ComponentZ.class);
}
}
这似乎也有点笨重,因为它需要使用儿童注射器等

再说一次,有更好的办法吗

也许我可以使用Netflix的Governator()将配置值注入到我的模块中(不确定这是否可行)


其他人如何处理这个问题?

我最近使用的应用程序有一个属性文件(或其他配置),用于确定应用程序的哪些部分是相关的。我们的典型方法是立即解析这些属性(仅解析到属性对象)并从中构造应用程序模块,然后根据指定的值有条件地包含其他模块

在一些地方,这已经发展成一个“init参数”集合,并列举了可能的参数:

enum InitParam {
    PricesQueue("prices.queue")
}
每个枚举实例都与属性键相关,并且有一种方法可以从属性中获取每个参数的基本字符串值:

boolean presentIn(Properties props) { return props.containsKey(propertyKey); }
String valueIn(Properties props) { return props.getProperty(propertyKey); }
因此,可以这样使用:

public AppModule extends AbstractModule {
    private final Properties config;
    protected void configure() {
        if (InitParam.PricesQueue.presentIn(config)) {
            install(new PricesQueueConsumerModule(config));
        }
    }
}
此外,还有一个模块将配置属性中的所有值绑定到
字符串
可选
等,允许:

@Inject
public PricesQueueConsumer(@FromInitParam(InitParam.PricesQueue) String queueName) {
}
这将在配置不可用时捕获正在引用的队列使用者(如果配置文件中不存在值,则模块不会绑定字符串),同时仍允许将值不存在时的行为推迟到以后(通过注入
可选


这有点类似于第二种方法,只是我没有考虑使用Guice注入Guice模块的方法,这似乎有点复杂。虽然可能本质上是一样的。也许您不需要父/子注入器,只需创建一个“引导”注入器来构建顶级应用程序模块,然后使用它来构建一个完全独立的注入器?

看来子注入器模式非常常见,可能是一种方法:是的,这似乎有点像建议的第二种方法。您的FromInitParam注释也有点像Netflix的governator库提供的配置注释,对吗?我确实喜欢将配置参数绑定到可选参数的功能,不过……我没有使用governator库:但是对于FromInitParam,我抄袭了Guice提供的命名注释的实现,只是让它采用枚举参数而不是字符串。