【问题标题】:Bindings in Guice: Compile time dependenciesGuice 中的绑定:编译时依赖
【发布时间】:2015-10-28 21:32:48
【问题描述】:

在探索 Guice 时,我对注入依赖项的方式有疑问。

根据我的理解,DI 的重要方面之一是,依赖项是已知的并且在运行时注入。

在 Guice 中,要注入依赖项,我们需要添加绑定或实现提供程序。添加依赖项需要一个类对象,该对象会在该类上添加编译时依赖项。避免这种情况的一种方法是将其实现为提供者,并让提供者使用反射来动态加载类。

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 选择类对象而不是类名有什么原因吗?那可能已经消除了编译时依赖,对吧?

【问题讨论】:

  • 老实说,我没明白。你能说得更具体点吗?
  • @MilanBaran 好的,让我再试一次。假设有三个组件 A、B 和 C。C 需要 A 和 B 的实现。那是我们将 A 和 B 的实现注入到 C。现在想象有一个 maven 模块 M1 想要注入 A 的实现和B 但它们归其他 maven 模块 M2 所有。要注入实现,模块 M 应该对模块 M2 有编译依赖。
  • spring 使用 XML 作为支持这一点的方法之一。您可以在 XML 中指定实现者。这个链接link 有一个这样的配置文件的例子。

标签: java dependency-injection guice


【解决方案1】:

如果您的 interfaceimplementation 定义在同一个依赖项中(即在同一个 JAR 文件中),那么您已经对实现具有硬构建依赖项,不管你是否使用 Guice。

基本上,只要你有:

public final class MyClass {
  public void doSomething(Foo foo);
}

然后要编译MyClassFoo 的定义需要在编译时类路径中。

解决这个问题的方法是将接口从实现中分离出来。例如,如果Foo 是一个接口,而FooImpl 是它的实现,您可以将FooImpl 放在与Foo 不同的依赖项(即不同的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,但它可以是FooImplSuperFoo

如果我们将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();
}

当然,您也可以使用您喜欢的任何其他机制来选择要安装的模块。在许多情况下,您甚至不想通过反思来执行此操作,您可以在更改构建依赖项的同时更改代码。

【讨论】:

  • 好的,这是有道理的。我们总是可以在实现中拥有模块。即使我们想要覆盖某些实现,我们也会使用适当的注释来实现它并从中创建一个新模块。您基本上是建议动态加载模块而不是加载实现。
猜你喜欢
  • 2023-03-04
  • 2019-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多