简体   繁体   English

使用 Multibinding 泛化 guice 的机器人腿示例

[英]Generalize guice's robot-legs example with Multibinding

I have this use case that is very similar to the robot-legs example of Guice, except I don't know how many "legs" I have.我有这个与 Guice 的机器人腿示例非常相似的用例,除了我不知道我有多少“腿”。 Therefore I can't use the annotations needed for the robot-legs example.因此我不能使用机器人腿示例所需的注释。

I expect to gather all these "legs" in an java.util.Set with Guice's Multibindings extension.我希望将所有这些“腿”收集在带有 Guice 的 Multibindings 扩展的 java.util.Set 中。

Technically, in a PrivateModule I would like to expose an implementation directly as an element of the set that will be provided by the Multibindings extension.从技术上讲,在 PrivateModule 中,我想将实现直接公开为将由 Multibindings 扩展提供的集合的元素。 I just don't know how to do that.我只是不知道该怎么做。

For reference and code example, see the robot-legs example here: http://code.google.com/p/google-guice/wiki/FrequentlyAskedQuestions#How_do_I_build_two_similar_but_slightly_different_trees_of_objec有关参考和代码示例,请参见此处的机器人腿示例: http://code.google.com/p/google-guice/wiki/FrequentlyAskedQuestions#How_do_I_build_two_similar_but_slightly_different_trees_of_objec


Here's my precise use-case:这是我的精确用例:

I have the following:我有以下内容:

// Main application
public interface MyTree {...}
public interface MyInterface {
  public MyTree getMyTree() {}
}
public abstract class MyModule extends PrivateModule {}
public class MyManager {
  @Inject MyManager (Set<MyInterface> interfaces){ this.interfaces = interfaces }
}
public class MainModule extends AbstractModule {
  public void configure () {
    // Install all MyModules using java.util.ServiceLoader.
  }
}


// In expansion "square.jar"
public class SquareTree implements MyTree {...}
public class SquareImplementation implements MyInterface {
  @Inject SquareImplementation (MyTree tree) { this.tree = tree; }
  public MyTree getMyTree () { return this.tree; }
}
public class SquareModule extends MyModule { // correctly defined as a ServiceLoader's service.
  public void configure () {
    // How to make this public IN a multibinder's set?
    bind(MyInterface.class).to(SquareImplementation.class);

    // Implementation specific to the Squareimplementation.
    bind(MyTree.class).to(SquareTree.class);
  }
}

// In expansion "circle.jar"
public class CircleTree implements MyTree {...}
public class CircleImplementation implements MyInterface {
  @Inject CircleImplementation (MyTree tree) { this.tree = tree; }
  public MyTree getMyTree () { return this.tree; }
}
public class CircleModule extends MyModule { // correctly defined as a ServiceLoader's service.
  public void configure () {
    // How to make this public IN a multibinder's set?
    bind(MyInterface.class).to(CircleImplementation.class);

    // Implementation specific to the Circle implementation.
    bind(MyTree.class).to(CircleTree.class);
  }
}

Since I'm speaking about expansion jars, I don't know them at first, I don't even know how many of them there exist: I need to load the MyModule s with juServiceLoader and each module should define a MyInterface implementation (these two parts are ok).由于我说的是扩展jars,所以一开始我并不认识它们,我什至不知道它们有多少个:我需要用juServiceLoader加载MyModule并且每个模块都应该定义一个MyInterface实现(这些两部分都可以)。

The issue is to get all the MyInterface implementations in one set (in MyManager ).问题是将所有MyInterface实现放在一组中(在MyManager中)。 How can I do that?我怎样才能做到这一点?


Solution, completely based upon Jesse's answer:解决方案,完全基于杰西的回答:

// Create the set binder.
Multibinder<MyInterface> interfaceBinder = Multibinder.newSetBinder(binder(), MyInterface.class, MyBinding.class);

// Load each module that is defined as a service.
for (final MyModule module : ServiceLoader.load(MyModule.class)) {

  // Generate a key with a unique random name, so it doesn't interfere with other bindings.
  final Key<MyInterface> myKey = Key.get(MyInterface.class, Names.named(UUID.randomUUID().toString()));
  install(new PrivateModule() {
    @Override protected void configure() {
      // Install the module as part of a PrivateModule so they have full hands on their own implementation.
      install(module);
      // Bind the unique named key to the binding of MyInterface.
      bind(myKey).to(MyInterface.class);
      // Expose the unique named binding
      expose(myKey);
    }
  });
  // bind the unique named binding to the set
  interfaceBinder.addBinding().to(myKey);
}

This allows me to not force the "customer" to extend a PrivateModule, but rather use any module implementation if MyModule is an interface that extends Module.这允许我不强制“客户”扩展 PrivateModule,而是在 MyModule 是扩展 Module 的接口时使用任何模块实现。

It looks like you're going to need to jump through some hoops to promite bindings from the private module so that they can be in the top-level injector's multibinding.看起来您需要跳过一些环节来促进来自私有模块的绑定,以便它们可以在顶级注入器的多重绑定中。

This should work:这应该有效:

public class SquareModule extends AbstractModule { // does not extend PrivateModule
  @Overide public void configure() {
    // this key is unique; each module needs its own!
    final Key<MyInterface> keyToExpose
        = Key.get(MyInterface.class, Names.named("square"));

    install(new PrivateModule() {
      @Override public void configure() {

        // Your private bindings go here, including the binding for MyInterface.
        // You can install other modules here as well!
        ...

        // expose the MyInterface binding with the unique key
        bind(keyToExpose).to(MyInterface.class);
        expose(keyToExpose);
      }
    });

    // add the exposed unique key to the multibinding
    Multibinder.newSetBinder(binder(), MyInterface.class).addBinding().to(keyToExpose);
  }
}

This workaround is necessary because multibindings need to happen at the top-level injector.这种解决方法是必要的,因为多重绑定需要在顶级注入器中发生。 But private module bindings aren't visible to that injector, so you need to expose them.但是该注入器看不到私有模块绑定,因此您需要公开它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM