Guice 中的绑定:编译时间依赖
Bindings in Guice: Compile time dependencies
在探索 Guice 时,我对注入依赖项的方式有疑问。
根据我的理解,DI 的重要方面之一是,依赖项是已知的并在运行时注入。
在 Guice 中,要注入依赖项,我们需要添加绑定或实现提供程序。添加依赖项需要一个 class 对象,该对象会在 class 上添加编译时依赖项。避免这种情况的一种方法是将其实现为提供者并让提供者使用反射来动态加载 class。
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(CreditCardProcessor.class).toProvider(
BofACreditCardProcessorProvider.class);
bind(CreditCardProcessor.class).annotatedWith(BofA.class).toProvider(
BofACreditCardProcessorProvider.class);
bind(CreditCardProcessor.class).annotatedWith(Amex.class).toProvider(
AmexCreditCardProcessorProvider.class);
}
@Provides
PaymentProcessor createPaymentProcessor() {
return new PayPalPaymentProcessor();
}
@Provides
PayPalPaymentProcessor createPayPalPaymentProcessor() {
return new PayPalPaymentProcessor();
}}
Guice 选择 class 对象而非 class 名称是否有原因?那可以删除编译时依赖吗?
如果你的interface和implementation是在同一个依赖项中定义的(也就是说,在同一个JAR文件中)那么你已经有对实现的硬构建依赖,无论你是否使用 Guice。
基本上,只要您拥有:
public final class MyClass {
public void doSomething(Foo foo);
}
然后编译 MyClass
Foo
的定义需要在编译时类路径上。
解决这个问题的方法是将接口从实现中分离出来。例如,如果 Foo
是一个接口,而 FooImpl
是它的实现,您可以将 FooImpl
放在与 [=15 不同的依赖项(即不同的 JAR 文件)中=].
现在,假设您在 Maven 中有两个子项目:
foo-api/
pom.xml
src/main/java/com/foo/Foo.java
foo-impl/
pom.xml
src/main/java/com/foo/FooImpl.java
绑定Foo
的Guice模块应该放在哪里?它不应该存在于 foo-api
项目中,它应该存在于 foo-impl
项目中,与 FooImpl
.
一起
现在假设您有一个单独的 Foo
实现(我们称之为 SuperFoo
),并且您的项目需要一个 Foo
,但它可以是 FooImpl
或 SuperFoo
.
如果我们将 SuperFoo
作为自己的项目:
super-foo/
pom.xml
src/main/java/com/super/foo/SuperFoo.java
src/main/java/com/super/foo/SuperFooModule.java
现在您的所有应用程序代码都可以简单地 @Inject Foo
并使用 foo.在您的 main()
方法中(或您创建 Injector
的任何地方),您需要决定是安装 FooModule
(来自 foo-impl
)还是 SuperFooModule
(来自 super-foo
).
那个是值得反思的地方。例如,您可以有一个配置标志 foo_module
,它可以设置为 "com.foo.FooModule"
或 "com.super.foo.SuperFooModule"
。您可以使用如下代码决定安装哪一个:
public static void main(String[] args) {
Config config = parseConfig(args);
List<Module> modules = new ArrayList<>();
modules.add(...); // application modules
String fooModuleName = config.get("foo_module");
Class<? extends Module> moduleClass =
Class.forName(fooModuleName).asSubclass(Module.class);
modules.add(moduleClass.newInstance());
Injector injector = Guice.createInjector(modules);
injector.getInstance(MyApplication.class).run();
}
当然,您也可以使用您喜欢的任何其他机制来select安装哪个模块。在许多情况下,您甚至不想反思地这样做,您可以在更改构建依赖项的同时简单地更改代码。
在探索 Guice 时,我对注入依赖项的方式有疑问。
根据我的理解,DI 的重要方面之一是,依赖项是已知的并在运行时注入。
在 Guice 中,要注入依赖项,我们需要添加绑定或实现提供程序。添加依赖项需要一个 class 对象,该对象会在 class 上添加编译时依赖项。避免这种情况的一种方法是将其实现为提供者并让提供者使用反射来动态加载 class。
public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(CreditCardProcessor.class).toProvider(
BofACreditCardProcessorProvider.class);
bind(CreditCardProcessor.class).annotatedWith(BofA.class).toProvider(
BofACreditCardProcessorProvider.class);
bind(CreditCardProcessor.class).annotatedWith(Amex.class).toProvider(
AmexCreditCardProcessorProvider.class);
}
@Provides
PaymentProcessor createPaymentProcessor() {
return new PayPalPaymentProcessor();
}
@Provides
PayPalPaymentProcessor createPayPalPaymentProcessor() {
return new PayPalPaymentProcessor();
}}
Guice 选择 class 对象而非 class 名称是否有原因?那可以删除编译时依赖吗?
如果你的interface和implementation是在同一个依赖项中定义的(也就是说,在同一个JAR文件中)那么你已经有对实现的硬构建依赖,无论你是否使用 Guice。
基本上,只要您拥有:
public final class MyClass {
public void doSomething(Foo foo);
}
然后编译 MyClass
Foo
的定义需要在编译时类路径上。
解决这个问题的方法是将接口从实现中分离出来。例如,如果 Foo
是一个接口,而 FooImpl
是它的实现,您可以将 FooImpl
放在与 [=15 不同的依赖项(即不同的 JAR 文件)中=].
现在,假设您在 Maven 中有两个子项目:
foo-api/
pom.xml
src/main/java/com/foo/Foo.java
foo-impl/
pom.xml
src/main/java/com/foo/FooImpl.java
绑定Foo
的Guice模块应该放在哪里?它不应该存在于 foo-api
项目中,它应该存在于 foo-impl
项目中,与 FooImpl
.
现在假设您有一个单独的 Foo
实现(我们称之为 SuperFoo
),并且您的项目需要一个 Foo
,但它可以是 FooImpl
或 SuperFoo
.
如果我们将 SuperFoo
作为自己的项目:
super-foo/
pom.xml
src/main/java/com/super/foo/SuperFoo.java
src/main/java/com/super/foo/SuperFooModule.java
现在您的所有应用程序代码都可以简单地 @Inject Foo
并使用 foo.在您的 main()
方法中(或您创建 Injector
的任何地方),您需要决定是安装 FooModule
(来自 foo-impl
)还是 SuperFooModule
(来自 super-foo
).
那个是值得反思的地方。例如,您可以有一个配置标志 foo_module
,它可以设置为 "com.foo.FooModule"
或 "com.super.foo.SuperFooModule"
。您可以使用如下代码决定安装哪一个:
public static void main(String[] args) {
Config config = parseConfig(args);
List<Module> modules = new ArrayList<>();
modules.add(...); // application modules
String fooModuleName = config.get("foo_module");
Class<? extends Module> moduleClass =
Class.forName(fooModuleName).asSubclass(Module.class);
modules.add(moduleClass.newInstance());
Injector injector = Guice.createInjector(modules);
injector.getInstance(MyApplication.class).run();
}
当然,您也可以使用您喜欢的任何其他机制来select安装哪个模块。在许多情况下,您甚至不想反思地这样做,您可以在更改构建依赖项的同时简单地更改代码。