【问题标题】:How to make factories work well with Guice?如何让工厂与 Guice 很好地协同工作?
【发布时间】:2019-09-09 19:45:34
【问题描述】:

在我的项目中,我到处使用依赖注入,并在两种情况下使用临时工厂。首先,当我想准确控制创建实例的时间时,我会注入工厂而不是实例:

// WidgetA must be created before WidgetB, because of the side-effects
// on the container.
WidgetAFactory.make(container);
WidgetBFactory.make(container);

另一种情况是构造函数采用可注入值和运行时值的混合。而不是使用:

@Inject
WidgetC(
    Container,
    @WidgetCFont Font,
    @WidgetCColor Color,
    @Named("flag") String flag) {
  ...
}

我用:

@Inject
WidgetCFactory(
    @WidgetCFont Font font,
    @WidgetCColor Color color,
    @Named("flag") String flag) {
  ...
}

WidgetCFactory.make(Container container) {
 return new WidgetC(container, font, color, flag);
}

但是我在使用工厂时遇到了两个限制:

  1. 在我的第一个示例中,我还需要 WidgetA 成为其他 @Injected 构造函数所需的 @Singleton。到目前为止,我的解决方案是将调用工厂时创建的实例存储起来,并@Provides 供其他人使用。有没有办法将这个单例的控制权交还给 guice,而不必自己维护那个实例?

  2. 在我的第二个示例中,管理注入的依赖项是一团糟:WidgetCFactory 必须使用一长串注入值调用 WidgetC 构造函数,必须针对依赖项中的每次更改进行更新,没有注释检查。有没有办法为 Guice 提供运行时参数,并让它处理其他依赖项?

感觉对于这两种情况,我都可以使用一个子注入器,它会被赋予运行时值,并让 Guice 成为工厂:

public static class WidgetCFactory {
  private final Injector injector;

  @Inject
  public WidgetCFactory(Injector injector) {
    this.injector = injector;
  }

  public WidgetC make(Container container) {
    Injector childInjector = injector.createChildInjector(new AbstractModule() {
      @Override protected void configure() {
        bind(Container.class).toInstance(container);
      }
    });
    return childInjector.getInstance(WidgetC.class);
  }
}

但我没有发现很多人这样做。是因为它太重了,还是超出了依赖注入的良好实践?有什么更好的方法?

【问题讨论】:

    标签: dependency-injection guice factory


    【解决方案1】:

    混合注入值和运行时值意味着您应该研究“辅助注入”,它允许您声明调用站点在运行时提供某些注入值,并生成一个工厂,该工厂仅将这些值作为参数公开。从https://github.com/google/guice/wiki/AssistedInject 开始,您需要为要以这种方式处理的每种类型安装一个模块,例如

    // this goes in your existing Module.configure()
    install(new FactoryModuleBuilder()
         // you can add more than one type here in this way
         .implement(WidgetC.class, WidgetC.class)
         .build(WidgetFactory.class));
    
    //...
    
    public interface WidgetFactory {
        // you can add more than one method here 
        WidgetC createWidgetC(Container container);
    }
    
    @AssistedInject
    WidgetC(
        @Assisted Container,
        @WidgetCFont Font,
        @WidgetCColor Color,
        @Named("flag") String flag) {
      ...
    }
    

    特别注意WidgetC构造函数的变化,构造函数上的不同注解(因为实际上通过正常注入构造并不安全)和Container参数(将由工厂提供) ,而不是 IoC 容器。


    要使 WidgetA 成为单例,您可以使用 @Singleton 装饰类型,或者将其绑定到您的 configure() 方法中:

    bind(WidgetA.class).in(Singleton.class);
    

    正如写的那样,它会被延迟创建——它只会在第一次被请求后才存在,但是每次被请求时,它都是同一个实例,不会从头开始创建。

    【讨论】:

    • 感谢 Colin,这正是我想要的! AssistedInject wiki 页面以与我提​​出问题的方式完全相同的方式呈现问题!
    【解决方案2】:

    回显Colin's correct answer,辅助注入是要走的路,Guice 从 2.0 开始提供辅助注入(通过单独的依赖项/JAR)。您可以在 Guice wiki AssistedInject page 上阅读有关 Guice 实现的更多信息,但除了 Colin 所写的内容之外,我不会有任何示例。

    您可以考虑的另一种选择是AutoFactory,它为您生成工厂实现的代码。 (它是 Google Auto 的一部分,这是一套 Java 代码生成器,用于创建注释实现、服务、不可变值对象和工厂。)它是 Dagger 的事实标准,但适用于包括 Guice 在内的任何 JSR-330 框架。


    关于您的问题 #1,我将与 Colin 不同,说您正在寻找的东西本质上有些危险:如果 @Singleton 对象在您的应用程序的整个生命周期中都存在,但您的 WidgetA Factory 需要一个容器,那么您的 WidgetA 可能会在您的 Container 准备好之前存在,或者在您的 Container 被销毁后存在。

    如果你的 WidgetA Container 也是@Singleton,那么你可以不用Factory 创建WidgetA,一切顺利:你可以跳过Factory,绑定Container,正常绑定WidgetA,注入Provider<WidgetA>(可以不使用额外的配置)来延迟 WidgetA 的创建,直到你准备好。

    如果您的真正要求是只要 Container 存在,WidgetA 就一直存在,但 WidgetA/B/C 在那时都使用相同的 Container 和 WidgetA,您可以考虑在哪里绑定您的 Container 的a child injector和小部件。这样每个 Container 都有自己的 WidgetA,WidgetA 的每次注入在该容器内都是一致的,当你获得一个新的 Container 时,你将处理 ​​WidgetA。当然,如果您的 Container 仅在您的 Injector 工作后才开始可用并且之后保持一致,您可以将该子注入器用作您的主注入器,然后让 WidgetA 工作。

    如果您的 WidgetA 依赖的 Container 开始时不可用,请注意:这可能是“范围扩大注入”,因为您的 Container 将在 WidgetA 中以 @Singleton 的形式存在,即使它本来是这样的被垃圾收集。这充其量可能是内存泄漏,并且可能会导致应用程序中存在多个容器时出现奇怪的错误。您可以像一直使用的那样使用有状态模块,但无论如何都要非常小心。

    【讨论】:

    • 同意,感谢您考虑相关问题,我只是将它们作为共享一些代码的各个点阅读。
    • 谢谢杰夫。非常好的观点,确实可以满足我对这个话题的想法。我喜欢将儿童注射器用作一致性气泡的想法。
    • 关于使用 AutoFactory 的好建议。 Guice 自己甚至推荐它:“考虑使用 AutoFactory 而不是 AsistedInject。AutoFactory 在概念上更容易理解并且需要的样板更少。”我已经改用 AutoFactory,一个很大的优势是您不必添加(非直接的)绑定。
    猜你喜欢
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-26
    • 2016-12-08
    • 2020-11-24
    • 1970-01-01
    相关资源
    最近更新 更多