使用注释来提供Google guice MapBinder

问题描述 投票:0回答:1

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

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

}

怎么样?

java annotations guice jsr330
1个回答
1
投票

如果没有一些昂贵的类路径扫描,这是不可能的:如果注入器没有对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不具备任何分区的分散性。不能给你。

© www.soinside.com 2019 - 2024. All rights reserved.