Java 如何指定GoogleGuice的实现路径?

Java 如何指定GoogleGuice的实现路径?,java,maven,ant,module,guice,Java,Maven,Ant,Module,Guice,我已经将我的解决方案从ant转移到maven。前面我在build-API和IMPL-jar之后有两个jar文件,这个jar是相互依赖的,因为ant并不禁止它。 Maven禁止交叉依赖,但我在API jar中有一些带有IMPL类的Guice模块。我无法将这些类移动到API,也无法更改这些模块。我想找到这样的解决方案: bind(BitTrackUtils.class). to(BitTrackUtilsBase.class). in(Scopes.SINGLETON

我已经将我的解决方案从ant转移到maven。前面我在build-API和IMPL-jar之后有两个jar文件,这个jar是相互依赖的,因为ant并不禁止它。 Maven禁止交叉依赖,但我在API jar中有一些带有IMPL类的Guice模块。我无法将这些类移动到API,也无法更改这些模块。我想找到这样的解决方案:

bind(BitTrackUtils.class).
        to(BitTrackUtilsBase.class).
        in(Scopes.SINGLETON);
但是使用实现路径而不是“BitTrackUtilsBase.class”。是否存在除反射之外的解决方案?也许是Guice注释之类的。。
谢谢。

当您真正想从一开始就将二者解耦时,API和实现通常就是这样设计的。我认为您应该尝试这样做,而不是试图修复笨拙的循环依赖关系。类要么是API的一部分,要么不是。下面的模式说明了这一点。可以先构建API,而不需要任何实现。所以Maven将非常乐意为您的构建提供帮助

magic-api.jar 这个JAR包含您的API,仅此而已。请注意,这里有一些类通常不属于API(例如,提供程序接口),它们确实属于API

magic-api.jar/
└─ magic/
   ├─ spi/
   |  └─ MagicProvider.class
   ├─ Magician.class
   └─ Trick.class
Trick.java 你的核心API

public interface Trick {
  void prepare();
  void execute();
}
MagicProvider.java 这是您的提供者,一个SPI(服务提供者接口),它允许注册您的实现。这个类是公开的(公共的),是的,但是在另一个包中,并且有文档记录说“这只适用于SPI”。您使用的是Guice,这一点您是知道的(好吧,在提供实现的方式上有些不同,但核心思想是相同的)

java 此类将允许您通过SPI访问
技巧
。这是一个基本示例,在整个运行过程中只使用一个
MagicProvider
,那么,您可以发挥创造性,寻找实现,直到找到适合您的实现。例如,如果您有
getTrick(String trickName)
您可以循环使用所有
MagicProvider
,直到有人能够提供名为
trickName
的技巧

public class Magician {
  private static final ServiceLoader<MagicProvider> providers = ServiceLoader.load(MagicProvider.class);

  public static Magician getInstance() {
    for(MagicProvider provider: providers) {
      if (provider != null) {
        return new Magician(provider);
      }
    }
    throw new RuntimeException("No implementation found for MagicProvider");
  }

  private final MagicProvider provider;
  private Magician (MagicProvider provider) {
    this.provider = provider;
  }
  public Trick getTrick() {
    return provider.getTrick();
  }
}
Copperfield.java 这是您的SPI实现。通常,它是公共的,具有公共构造函数,因此
ServiceLoader
可以正确地加载它。这是您将使用Guice的地方,而不是在API中

public class Copperfield implements MagicProvider {
  private final Injector injector;
  public Copperfield() {
    injector = Guice.createInjector(new CopperfieldModule());
  }
  public Trick getTrick() {
    return injector.getInstance(Trick.class);
  }
}
copperfiedmodule.java 您的标准、基本GUI模块

class CopperfieldModule extends AbstractModule {
  @Override public void configure() {
    bind(Trick.class).to(HideTheStatueOfLiberty.class).in(Scopes.SINGLETON);
  }
}
隐藏liberty.java 这是API的真正实现

public class HideTheStatueOfLiberty implements Trick {
  @Override public void prepare() {
    System.out.println("Now you see the Statue of Liberty.");
  }
  public void execute() {
    System.out.println("Now you don't!");
  }
}
magic.spi.MagicProvider 是的,这是全名,没有其他扩展名(没有
.txt
.java
.class
,…)。该名称也是提供者接口的完全限定Java名称(意思是“带包”)。这只是一个包含以下内容的文本文件。不多不少。确保将其置于
/META-INF/services/
中。您可以选择添加带有
#
符号的注释

# Register Copperfield as a provider
copperfield.spi.Copperfield
用法 这将出现在API客户端的某个地方。注意,这里只看到API中的类

Magician magician = Magician.getInstance();
Trick trick = magician.getTrick();
trick.prepare();
trick.execute();
结果 笔记
  • 如果你不想自己在
    /META-INF/services/
    中声明一个文件,你可以看看优秀的(谷歌也提供的)。在这种情况下,您只需将
    @AutoService(MagicProvider)
    添加到
    Copperfield
    类中,然后忘记“无扩展”文件

  • 为什么要将模块保存在API JAR中?如果有人想编写自己的接口实现,那么它必须与Guice模块以及Guice依赖项一起导入。非常感谢。但是你知道我如何通过GOOGLE GUICE SPI将我的模块从IMPL模块放到API模块中的注入器吗?@MagentaMill这打破了API与其实现分离的原则。我看到的是在实现中编写一个名为
    /META-INF/services/com.google.inject.Module
    的服务文件,其中包含要实例化的每个GUI模块。在您的API GUI模块中有以下代码:
    for(module m:ServiceLoader.load(module.class))install(m)。但我不建议这样做。我建议在API及其实现之间进行清晰的分离。这就是JavaSPI,对吗?那Guice SPI呢?我不知道如何在我的情况下使用它。因为我的根guice模块在IMPL中,这是安装所有其他模块。我可能会通过反射创建这个模块,而不使用JavaSPI——这很简单。你知道,如果没有具体的例子,我们会有点拘泥于你所拥有的和你所需要的。我试图向您展示如何正确地将API与impl解耦,而彼此之间不产生任何影响。但是你没有写任何我们可以用的东西。我试着猜你想要什么,但显然我错了。正如您所知,Guice SPI仅用于创建Guice扩展。最好的方法是“JavaSPI”,而不是“GuiceSPI”。我提到后者只是为了告诉你SPI并不罕见。
    
    # Register Copperfield as a provider
    copperfield.spi.Copperfield
    
    Magician magician = Magician.getInstance();
    Trick trick = magician.getTrick();
    trick.prepare();
    trick.execute();
    
    Now you see the Statue of Liberty.
    Now you don't!