如何指定Google Guice 执行路径?
How to specify Google Guice path to implementation?
我已将我的解决方案从 ant 转移到 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 注释什么的..
谢谢。
这就是 API 和实现的通常设计方式,当您真正想要从一开始就将两者分离时。我认为你应该尝试这样做而不是试图修复你笨拙的循环依赖。 类 要么是 API 的一部分,要么不是。以下架构清楚地说明了这一点。并且 API 可以先构建而不需要任何实现。因此,Maven 将非常乐意为您提供构建服务。
魔法-api.jar
此 JAR 包含您的 API,仅此而已。请注意,您会发现这里有一些 classes 通常不属于 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(服务提供者接口),它允许您的实现被注册。这个 class 是暴露的(public),是的,但是在另一个包中并且记录在 "this is for SPI only" 中说了些什么。您正在使用 Guice,嗯,知道 Guice has a SPI(好吧,在提供实现方式方面有些不同,但核心思想是相同的)。
public interface MagicProvider {
Trick getTrick();
}
Magician.java
此 class 将允许您通过 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 的接口。完成后,只需在与提供者 class 名称完全相同的文件中记下提供者名称(在我们的示例中为 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 实现。通常,它是 public,带有 public 构造函数,以便 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!
的 classes
Magician magician = Magician.getInstance();
Trick trick = magician.getTrick();
trick.prepare();
trick.execute();
结果
Now you see the Statue of Liberty.
Now you don't!
备注
- 如果你不喜欢自己在
/META-INF/services/
中声明一个文件,你可以看看优秀的AutoService(也是Google)。在这种情况下,您需要做的就是将 @AutoService(MagicProvider)
添加到 Copperfield
class 并忘记 "extensionless" 文件。
我已将我的解决方案从 ant 转移到 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 注释什么的.. 谢谢。
这就是 API 和实现的通常设计方式,当您真正想要从一开始就将两者分离时。我认为你应该尝试这样做而不是试图修复你笨拙的循环依赖。 类 要么是 API 的一部分,要么不是。以下架构清楚地说明了这一点。并且 API 可以先构建而不需要任何实现。因此,Maven 将非常乐意为您提供构建服务。
魔法-api.jar
此 JAR 包含您的 API,仅此而已。请注意,您会发现这里有一些 classes 通常不属于 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(服务提供者接口),它允许您的实现被注册。这个 class 是暴露的(public),是的,但是在另一个包中并且记录在 "this is for SPI only" 中说了些什么。您正在使用 Guice,嗯,知道 Guice has a SPI(好吧,在提供实现方式方面有些不同,但核心思想是相同的)。
public interface MagicProvider {
Trick getTrick();
}
Magician.java
此 class 将允许您通过 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 的接口。完成后,只需在与提供者 class 名称完全相同的文件中记下提供者名称(在我们的示例中为 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 实现。通常,它是 public,带有 public 构造函数,以便 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!
的 classesMagician magician = Magician.getInstance();
Trick trick = magician.getTrick();
trick.prepare();
trick.execute();
结果
Now you see the Statue of Liberty.
Now you don't!
备注
- 如果你不喜欢自己在
/META-INF/services/
中声明一个文件,你可以看看优秀的AutoService(也是Google)。在这种情况下,您需要做的就是将@AutoService(MagicProvider)
添加到Copperfield
class 并忘记 "extensionless" 文件。