在Java项目中,使用Google Guice使用Gradle 5.2构建。
我使用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 {
}
怎么样?
如果没有一些昂贵的类路径扫描,这是不可能的:如果注入器没有对Twix类的任何引用,它将无法将其绑定到Map中而不扫描类路径上的每个JAR以搜索@SnackImpl
注释类。您可以尝试使用Guava's ClassPath,但如果您使用基于网络或自定义的类加载器,这可能根本不易处理。无论如何我不会推荐它。
另一种方法是使用Java内置的ServiceLoader框架,该框架允许单个JAR列出给定服务(接口)的完全限定的实现。您甚至可以根据注释将Google的Auto框架用于generate that service file for you。
它负责列出实现,但是你仍然需要将它们绑定到MapBinder中。幸运的是,MapBinder不需要单个定义,并且会在模块构建期间自动合并多个MapBinder定义:
支持从不同模块贡献地图绑定。例如,可以让CandyModule和ChipsModule都创建自己的MapBinder,并为每个提供绑定到小吃地图。注入该映射时,它将包含来自两个模块的条目。
(来自MapBinder docs)
考虑到这一点,我建议每个插件包获得自己的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不具备任何分区的分散性。不能给你。