Обобщить пример ноги робота с помощью Multibinding

У меня есть этот пример использования, который очень похож на пример с роботизированными ногами в Guice, за исключением того, что я не знаю, сколько у меня "ног". Поэтому я не могу использовать аннотации, необходимые для примера с ножками робота.

Я ожидаю собрать все эти "ноги" в java.util.Set с расширением Guice's Multibindings.

Технически, в PrivateModule я хотел бы представить реализацию непосредственно как элемент набора, который будет предоставлен расширением Multibindings. Я просто не знаю, как это сделать.

Для справки и примера кода см. Пример с ножками робота здесь: http://code.google.com/p/google-guice/wiki/FrequentlyAskedQuestions


Вот мой точный вариант использования:

У меня есть следующее:

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

Поскольку я говорю о банках расширения, сначала я их не знаю, даже не знаю, сколько их существует: мне нужно загрузить MyModuleс j.u.ServiceLoader и каждый модуль должен определить MyInterface реализация (эти две части в порядке).

Вопрос в том, чтобы получить все MyInterface реализации в одном наборе (в MyManager). Как я могу это сделать?


Решение, полностью основанное на ответе Джесси:

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

Это позволяет мне не заставлять "клиента" расширять PrivateModule, а использовать любую реализацию модуля, если MyModule - это интерфейс, расширяющий модуль.

1 ответ

Решение

Похоже, что вам нужно будет перепрыгнуть через несколько обручей, чтобы получить привязки из частного модуля, чтобы они могли быть в мультисвязке инжектора верхнего уровня.

Это должно работать:

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

Этот обходной путь необходим, потому что мультииндикация должна происходить на инжекторе верхнего уровня. Но привязки приватных модулей не видны для этого инжектора, поэтому вам нужно их выставить.

Другие вопросы по тегам