0

在 Java 项目中,使用 Gradle 5.2 和 Google Guice 进行构建。

我使用MapBinderhttp://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 {

}

如何?

4

1 回答 1

1

如果没有一些昂贵的类路径扫描,这将是不可能的:如果注入器没有对您的 Twix 类的任何引用,它就无法在不扫描类路径上的每个 JAR 的情况下将其绑定到 Map 中@SnackImpl-带注释的类。您可以尝试使用Guava 的 ClassPath,但如果您使用基于网络或自定义的类加载器,这可能根本无法处理。无论如何,我不会推荐它。

一种替代方法是使用 Java 的内置ServiceLoader框架,该框架允许各个 JAR 列出给定服务(接口)的完全合格的实现。您甚至可以使用 Google 的 Auto 框架根据注释为您生成该服务文件。

这需要列出实现,但您仍然需要将它们绑定到 MapBinder。幸运的是,MapBinder 不需要单个定义,并且会在模块构建期间自动合并多个 MapBinder 定义:

支持从不同模块贡献映射绑定。例如,可以让 CandyModule 和 ChipsModule 都创建自己的 MapBinder,并各自为小吃地图提供绑定。注入该映射后,它将包含来自两个模块的条目。

(来自MapBinder 文档

考虑到这一点,我建议每个插件包都有自己的 Guice 模块,并在其中注册到 MapBinder,然后使用 ServiceLoader 将这些 Guice 模块添加到主注入器,以便在注入器创建时获取这些模块。

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

或者,您可以直接使用 AutoService 列出 Twix 之类的实现,然后创建一个模块,将所有 ServiceLoader 实现读入您的 MapBinder,但这可能会限制您的插件的灵活性,并且不会让您获得 MapBinder 没有的绑定的任何分散化还没给你。

于 2019-02-27T23:27:53.043 回答