Как сделать так, чтобы Guice связывал этот универсальный файл правильно? - PullRequest
0 голосов
/ 11 июня 2018

Я работаю на платформе для обработки сообщений, и у меня возникла проблема с привязками Guice.Я написал пример проекта, который отображает проблему, с которой я столкнулся, и это исключение выглядит следующим образом:

1) com.icacarealign.worker_example.PreprocessorMiddleware<MessageType> cannot be used as a key; It is not fully specified.

Вот код:

Интерфейсы

public interface Middleware {
  void configure();
}

public interface PreprocessorMiddleware<MessageType> extends Middleware {
  void prework(Context<MessageType> contextObj);
}

public interface PostprocessorMiddleware<MessageType> extends Middleware {
  void postwork(Context<MessageType> contextObj);
}

Связывание Guice

public class MiddlewareModule<MessageType> extends AbstractModule {
  private final List<Class<? extends PostprocessorMiddleware<MessageType>>> _postprocessors;
  private final List<Class<? extends PreprocessorMiddleware<MessageType>>> _preprocessors;

  public MiddlewareModule(
    List<Class<? extends PreprocessorMiddleware<MessageType>>> preprocessors,
    List<Class<? extends PostprocessorMiddleware<MessageType>>> postprocessors
  ) {
    _preprocessors = preprocessors;
    _postprocessors = postprocessors;
  }

  @Override
  protected void configure() {
    bindAllMiddleware(_preprocessors, new TypeLiteral<PreprocessorMiddleware<MessageType>>() {});
    bindAllMiddleware(_postprocessors, new TypeLiteral<PostprocessorMiddleware<MessageType>>() {});
  }

  private <T extends Middleware> void bindAllMiddleware(List<Class<? extends T>> middleware, TypeLiteral<T> type) {
    Multibinder<T> multibinder = Multibinder.newSetBinder(binder(), type);

    middleware.forEach(middlewareType -> bindMiddleware(multibinder, middlewareType));
  }

  private <T extends Middleware> void bindMiddleware(Multibinder<T> binder, Class<? extends T> type) {
    binder().bind(type).in(Singleton.class);
    binder.addBinding().to(type);
  }
}

Основной метод

public class Main {
  public static void main(String[] args) {
    List<Class<? extends PreprocessorMiddleware<Message>>> preprocessorMiddlewares = new ArrayList<>();
    List<Class<? extends PostprocessorMiddleware<Message>>> postprocessorMiddlewares = new ArrayList<>();

    preprocessorMiddlewares.add(ArbitraryPrepreprocessorMiddleware.class);
    postprocessorMiddlewares.add(ArbitraryPostprocessorMiddleware.class);

    MiddlewareModule<Message> module = new MiddlewareModule<>(preprocessorMiddlewares, postprocessorMiddlewares);

    Injector injector = Guice.createInjector(module);
  }
}

Что я делаю не так?

Ответы [ 2 ]

0 голосов
/ 12 июня 2018

Как описывает jacobm, вам нужно обойти дженерики и, возможно, потребуется передать тип, который вы связываете.К счастью, вы можете использовать Guice's Types.newParameterizedType для создания полностью определенного TypeLiteral.

public class MiddlewareModule<MessageType> extends AbstractModule {
  private final Class<MessageType> _clazz;
  private final List<Class<? extends PostprocessorMiddleware<MessageType>>> _postprocessors;
  private final List<Class<? extends PreprocessorMiddleware<MessageType>>> _preprocessors;

  public MiddlewareModule(
    // Accept the message type in a way that survives erasure.
    Class<MessageType> clazz,
    List<Class<? extends PreprocessorMiddleware<MessageType>>> preprocessors,
    List<Class<? extends PostprocessorMiddleware<MessageType>>> postprocessors
  ) {
    _clazz = clazz;
    _preprocessors = preprocessors;
    _postprocessors = postprocessors;
  }

  @Override
  protected void configure() {
    // Use the Class to create your fully-specified TypeLiteral.
    bindAllMiddleware(_preprocessors,
        Types.newParameterizedType(PreprocessorMiddleware.class, _clazz));
    bindAllMiddleware(_postprocessors,
        Types.newParameterizedType(PostprocessorMiddleware.class, _clazz));
  }

  private <T extends Middleware> void bindAllMiddleware(List<Class<? extends T>> middleware, TypeLiteral<T> type) {
    Multibinder<T> multibinder = Multibinder.newSetBinder(binder(), type);

    middleware.forEach(middlewareType -> bindMiddleware(multibinder, middlewareType));
  }

  private <T extends Middleware> void bindMiddleware(Multibinder<T> binder, Class<? extends T> type) {
    bind(type).in(Singleton.class);  // Don't call binder() explicitly.
    binder.addBinding().to(type);
  }
}

Обратите внимание, что это непроверенный код, так как у меня нет полного SSCCE;вам может потребоваться настроить параметры типа, чтобы убедить Guice в том, что использование шаблонов в шаблонах безопасно.

0 голосов
/ 11 июня 2018

Нельзя использовать переменную типа (в данном случае MessageType) в TypeLiteral с целью привязки Guice.Вместо этого вам необходимо выполнить рефакторинг для передачи TypeToken, который создается без использования переменных типа (вероятно, это означает передачу его с места вызова).

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

...