Guice:注入一个字符串的 ArrayList

Guice : Inject an ArrayList of Strings

我正在尝试在 Guice 的帮助下注入 ArrayListString。我想显示一个包含许多 RadioButton(例如)的面板,用户可以在其中 select 激活一些服务。

一旦 selected,我想获取所有 selected 服务的名称并将它们添加到列表中,并将此列表注入负责创建服务的经理。这是一个例子:

public class UIProviderModule extends ProviderServiceModule {
    private ArrayList<String> requestedServices;

    public UIProviderModule(ArrayList<String> requestedServices) {
        this.requestedServices = requestedServices;
    }

    @Override
    protected void configure() {
        bindConstant().annotatedWith(Names.named(Constants.REQ_SERVICES)).to(requestedServices);
        bind(IParser.class).to(UIParser.class);
        super.configure();
    }

}

我看过很多关于 Multibindings 和供应商的帖子,但我不明白这对我有什么帮助。我只想检索名称,因为我没有使用必须绑定到接口的 类。我错过了什么吗?

注意:我知道这可能不是 使用 Guice 的好方法,因为我将列表绑定到 Module.

我认为您误解了模块应该如何工作。

模块不创建对象,模块定义对象在需要时如何创建的规则。

MapBinder 会有所帮助的原因是您可以在单选按钮列表中定义 所有 服务,然后使用注入的映射来激活那些服务你需要。

下面是一些代码来说明我的意思:

public class ServiceModule extends AbstractModule {
  protected void configure() {
    MapBinder<String, Service> mapbinder
        = MapBinder.newMapBinder(binder(), String.class, Service.class);
    mapbinder.addBinding("service1").to(Service1.class).in(Singleton.class);
    mapbinder.addBinding("service2").to(Service2.class);
    // Define ALL the services here, not just the ones being used.
    // You could also look this up from a ClassLoader or read from a configuration file if you want
  }
}

然后,将 MapBinder 注入到您的 ServiceManager class - 不是 模块:

public class ServiceManager {
  private final Map<String, Service> serviceMap;

  @Inject
  public ServiceManager(Map<String, Service) serviceMap) {
    this.serviceMap = serviceMap;
  }

  // This is just one way to do it. It depends on how your UI works
  public void startAll(List<String> serviceList) {
    for(String serviceName : serviceList) {
      serviceMap.get(serviceName).start();
    }
  }
}

Guice 很容易做到:

bind(new TypeLiteral<List<String>>() {})
    .annotatedWith(Names.named(Constants.REQ_SERVICES))
    .toInstance(requestedServices);

请注意,为了绑定 List<String> 而不 Java 擦除泛型,您创建了一个短暂的匿名内部类型(TypeLiteral 的子类,主体为空 {}) .您还使用 toInstance.

让模块接受用于绑定的参数没有错,我更喜欢这样做而不是多绑定,因为所有绑定都可以轻松收集在一个地方。

请注意,您接受的 ArrayList<String> 是可变的,因此如果您将其注入多个位置,一个消费者可能会为其他所有人永久更改列表。使用 Guava 的 ImmutableList.copyOf(list)Collections.unmodifiableList(list) 可能更有意义(尽管如果模块创建者稍后更改传入列表,后者仍会让列表更改)。


关于您建议的应用程序生命周期,请记住 Guice 的 bindings 应该在注入器创建后或多或少保持不变。您描述的生命周期可以通过几种方式理解:

  • 在没有 Guice 帮助的情况下显示您的对话框,然后使用所选选项创建注入器
  • 注入所有选项的 List<String>,显示对话框,然后传递列表
  • 用所有选项注入您的 List<String>,显示对话框,然后创建一个包含您选择的选项列表的 子注入器

但是,所有这些都是可行的,具体取决于您希望完整列表和选定列表在您的应用程序中的可访问性。