如何指定Google Guice实现路径?

时间:2017-04-05 14:37:25

标签: java maven ant module guice

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

bind(BitTrackUtils.class).
        to(BitTrackUtilsBase.class).
        in(Scopes.SINGLETON);

但是实现路径而不是“BitTrackUtilsBase.class”。是否存在除反射之外的解也许,Guice注释或者什么.. 谢谢。

1 个答案:

答案 0 :(得分:3)

当你真正想要从头开始解耦时,通常会设计API和实现。我认为你应该试着这样做,而不是试图修复你笨重的循环依赖。类是API的一部分,或者不是。以下模式清楚地表明了这一点。并且可以首先构建API,而无需实现任何内容。所以Maven非常乐意帮你建立。

魔api.jar文件

此JAR包含您的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,嗯,知道Guice has a SPI(好吧,在实现方式方面有些不同,但核心思想是一样的。)

public interface MagicProvider {
  Trick getTrick();
}

Magician.java

该类允许您通过SPI访问Trick。这有一个基本的例子,在整个运行过程中只会使用一个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();
  }
}

魔impl.jar中

这个JAR有点不同:在导入之外没有提到API。所以在这里,责任只是从API实现接口。完成后,只需在名为Provider类名称的文件中写下Provider名称(在我们的例子中为magic.spi.MagicProvider)。该文件必须位于JAR文件的/META-INF/services/文件夹中。这基本上是您注册提供商的唯一约束。

magic-api.jar/
├─ copperfield/
|  ├─ spi/
|  |  ├─ Copperfield.class
|  |  └─ CopperfieldModule.class
|  └─ HideTheStatueOfLiberty.class
└─ META-INF/
   └─ services/
      └─ magic.spi.MagicProvider

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

CopperfieldModule.java

您的标准基本Guice模块。

class CopperfieldModule extends AbstractModule {
  @Override public void configure() {
    bind(Trick.class).to(HideTheStatueOfLiberty.class).in(Scopes.SINGLETON);
  }
}

HideTheStatueOfLiberty.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,...)。该名称也是Provider接口的完全限定Java名称(表示“with package”)。这只是一个包含以下内容的文本文件。没有更多,没有更少。务必将其放在/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();

结果

Now you see the Statue of Liberty.
Now you don't!

注释

  1. 如果您不想自己在/META-INF/services/中声明文件,可以查看优秀的AutoService(Google也是如此)。在这种情况下,您需要做的就是将@AutoService(MagicProvider)添加到Copperfield类并忘记“无扩展名”文件。