Предоставить экземпляр при условии, что другой модуль пока не предоставляет этот экземпляр. - PullRequest
5 голосов
/ 11 июля 2019

Мое приложение использует Guice for Dependency Injection и состоит из нескольких модулей, некоторые из которых зависят от экземпляра класса X, а некоторые должны иметь возможность работать независимо от основного приложения.Поэтому в MainModule я должен предоставить экземпляр класса X, в то время как некоторые подмодули также должны предоставить этот экземпляр, поскольку их соответствующие приложения должны иметь возможность работать без MainModule, предоставляющего указанный экземпляр класса X. Что приводит к ошибкампотому что «экземпляр класса X уже связан».

Я уже некоторое время осматриваюсь, но в основном я нахожу ссылки на PrivateModules, которые на самом деле не делают то, что мне нужно, также я нашел многона OptionalBindings, которые, насколько я понимаю, в основном предоставляют значения по умолчанию.

Что мне нужно, это своего рода условное связывание, как в «Если другой модуль предоставляет экземпляр класса X, ничего не делать, если никакой другой модуль не предоставляетэкземпляр класса X предоставляет этот. "

Ответы [ 3 ]

0 голосов
/ 19 июля 2019

Я думаю, что вы можете использовать модуль равенства для удовлетворения этой потребности:

Guice добавляет каждый модуль, который вы install, в Set, что приводит к дедупликации любых модулей, которые установлены с избыточностью, если они считают equal.

Таким образом, вы можете встроить привязку класса X в его собственный Module и убедиться, что у его модуля-класса есть метод equals, который будет идентифицировать другие экземпляры того же модуля как равные.

Например:

class XModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(X.class).to(RealX.class); // whatever you need to deduplicate
  }

  @Override
  public boolean equals(Object other) {
    return other == this || (other instanceof XModule);
  }

  @Override
  public int hashCode() {
    return XModule.class.hashCode();
  }
}

class ModuleA extends AbstractModule {
  @Override
  protected void configure() {
    install(new XModule());
  }
}

class ModuleB extends AbstractModule {
  @Override
  protected void configure() {
    install(new ModuleA());
    install(new XModule()); // will be deduplicated
  }
}

(это также обсуждается здесь: Убедитесь, что модуль загружается только один раз )

0 голосов
/ 25 июля 2019

Изоляция

Проблема вызвана недостаточной изоляцией различных подсистем.С точки зрения подсистемы S, тип X со временем изменил свой контракт.В прошлом тип давал S возможность контролировать все свои экземпляры.Но с интеграциями контроль экземпляров был потерян.Тип X изменил свое поведение несовместимым образом.

Подсистема S должна была быть более изолированной.Он не должен использовать типы, которые могут конфликтовать с другими системами.Вместо этого он должен был использовать свои собственные закрытые типы, тогда проблем не было бы.

Теперь, скажем, в нашей гипотетической проблеме есть подсистема Database.Он использует тайм-аут при отправке сетевых вызовов.Он хочет экземпляр Timeout, который имеет значение 100 миллис.Подсистема EmailSender ожидает замедления.Его Timeout должно равняться 5 секундам.При интеграции системы конфликтуют.

Один из подходов: частные типы обёрток

    // EmailSender-private wrapper. Class is not public on purpose.
    class EmailSenderTimeout {
      final Timeout timeout;
      EmailSenderTimeout(Timeout t) { this.timeout = t; }
    }

    // In the module
    bind(EmailSenderTimeout.class)
        .toInstance(new EmailSenderTimeout(Timeout.seconds(5));

    // In the service
    @Inject
    EmailSendingService(EmailSenderTimeout timeout) {
      long millis = timeout.timeout.millis();
    }

Уолла! Если кто-нибудь когда-нибудь придет и свяжет Timeout с тем, что пожелает его сердце,у нас EmailSender еще есть 5 секунд!

Мы добились изоляции.Мы все еще разделяем тип Timeout, но больше не делимся экземплярами.

Guicier: привязка аннотаций

Этот механизм является ответом Guice на нашу точную проблему.

    // Define this annotation once in the sub-system somewhere.
    // Perhaps even directly in the Module class.
    @Retention(RetentionPolicy.RUNTIME)
    @BindingAnnotation
    @interface ForEmail { }

    // EmailModule
    protected void configure() {
      bind(Timeout.class).annotatedWith(ForEmail.class)
          .toInstance(Timeout.seconds(5);
    }

    // Service
    class EmailSendingService {
      @Inject
      EmailServiceImpl(@ForEmail Timeout timeout) {
        long millis = timeout.millis();
      }
    }

Вы также можете повторно использовать аннотацию для других общих типов:

    class EmailServiceImpl {
      @Inject
      EmailServiceImpl(@ForEmail Timeout timeout, 
          @ForEmail RemoteAddress remoteAddress, 
          @ForEmail Protocol protocol) {
      }
    }

Каждая подсистема объявляет свою собственную аннотацию привязки и использует ее повсюду.

В абсолютном выражении никакие две подсистемы не должны связывать одни и те же типы , независимо от того, интегрированы они сегодня или нет.

Упрощенная ментальная модель Guice

Никогда не должно быть дубликатов в bindings:

    class Guice {
      HashMap<Key, Provider> bindings;
    }

    // Combines 3 things: Class, Generic Types, and Annotation
    class Key {
      Class<?> actualClass;
      @Nullable Class<?> annotationClass;
      @Nullable Type genericTypes;
    }

Подробнее: Key.java , TypeLiteral.java

0 голосов
/ 12 июля 2019

https://google.github.io/guice/api-docs/latest/javadoc/com/google/inject/util/Modules.html

Возможно, вам нужны методы переопределения

...