Используйте аннотацию для подачи Google Guice MapBinder

В проекте Java, соберите с Gradle 5.2, используя Google Guice.

Я использую MapBinder ( http://google.github.io/guice/api-docs/latest/javadoc/com/google/inject/multibindings/MapBinder.html):

MapBinder<String, Snack> mapbinder
         = MapBinder.newMapBinder(binder(), String.class, Snack.class);
     mapbinder.addBinding("twix").toInstance(new Twix());
     mapbinder.addBinding("snickers").toProvider(SnickersProvider.class);
     mapbinder.addBinding("skittles").to(Skittles.class);

Это работает нормально, но теперь я хочу "архитектуру плагинов", поэтому избегайте импорта всех классов Snack, а скорее объявляйте это непосредственно в классе, например:

@SnackImpl("Twix")
class Twix extends Snack {

}

Как?

1 ответ

Решение

Это не будет возможно без дорогостоящего сканирования пути к классам: если у инжектора нет ссылки на ваш класс Twix, он не сможет связать его с картой без сканирования каждого JAR-файла на пути к классам в поисках @SnackImplАннотированные классы. Вы можете попробовать это с помощью ClassPath Guava, но если вы используете сетевой или пользовательский загрузчик классов, это может вообще не быть отслеживаемым. В любом случае я бы не рекомендовал это.

Одна альтернатива состоит в том, чтобы использовать встроенную в Java инфраструктуру ServiceLoader, которая позволяет отдельным JAR-файлам перечислять полностью определенные реализации для данной службы (интерфейса). Вы даже можете использовать Google Framework для создания этого сервисного файла для вас на основе аннотаций.

Это поможет составить список реализаций, но вам все равно нужно будет связать их в MapBinder. К счастью, MapBinder не требует одного определения и автоматически объединит несколько определений MapBinder во время создания модуля:

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

(из документации MapBinder)

Имея это в виду, я бы порекомендовал, чтобы каждый комплект плагинов получал свой собственный модуль Guice, где он регистрируется в MapBinder, а затем вы добавляете эти модули Guice в основной инжектор, используя ServiceLoader, чтобы получить эти модули во время создания инжектора.

// Assume CandyPluginModule extends AbstractModule

@AutoService(CandyPluginModule.class)
public TwixPluginModule extends CandyPluginModule {
  @Override public void configure() {
    MapBinder<String, Snack> mapBinder
       = MapBinder.newMapBinder(binder(), String.class, Snack.class);
    mapBinder.addBinding("twix").to(Twix.class);
  }
}

Вы также можете воспользоваться суперклассом:

@AutoService(CandyPluginModule.class)
public TwixPluginModule extends CandyPluginModule {
  @Override public void configureSnacks() {  // defined on CandyPluginModule
    bindSnack("twix").to(Twix.class);
  }
}

В качестве альтернативы, вы можете перечислить реализации, такие как Twix, непосредственно с AutoService, а затем создать Модуль, который считывает все реализации ServiceLoader в ваш MapBinder, но это может ограничить гибкость ваших плагинов и не даст вам никакой децентрализации привязок, которые MapBinder не делает. уже не дам.

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