【问题标题】:Guice injector.getInstance() - good practice?Guice injector.getInstance() - 好的做法?
【发布时间】:2018-01-15 09:24:40
【问题描述】:

假设我有两个应用程序共享同一个库。这个库包含常见的类,如 DAO、Utils 等。共享库中的所有内容都与 Guice 连接。我的两个应用程序依赖于这个库,但不直接依赖于 Guice。

 ______    ______    ______
|      |  |      |  |      |
| APP1 |->| LIB  |<-| APP2 |
'------'  '------'  '------'

我目前使用这样的东西:

static <T> Utils.getInstanceOf (Class<T> type);

这只是一个包装器:

injector.getInstance (Class<T> type);

但是 guice 文档说:

在可行的情况下,避免使用这种方法,而倾向于使用 Guice 提前注入你的依赖。

那么,为这两个应用程序提供依赖注入而无需在 Guice 模块中手动绑定它们的最佳方式是什么?

【问题讨论】:

  • 你的设计是否排除了@Inject的使用?
  • 一点也不。我在共享库中使用它。忘了提到客户端应用程序中有 servlet 和 Jersey REST 类 - 即在 servlet 容器(非 JavaEE)中运行的 webapps。

标签: java dependency-injection guice


【解决方案1】:

那么为两者提供依赖注入的最佳方式是什么 无需在 Guice 模块中手动绑定应用程序?

没有这样的方法。您要么完全接受 Guice,要么不使用它并显式传递您的依赖项。好吧,以这种方式构造代码,因此您永远不会直接创建类依赖项,而是通过构造函数传递它们,也可以称为“依赖注入”,但我确定这不是您的意思。如果您不想在您的应用程序中使用 Guice,那么您将无法获得比 getInstance() 更好的东西,这很丑陋,尤其是因为您正在使用 static 包装器。

理想情况下,您的库应该提供一个模块,您可以通过 Guice.createInjector() 在您的应用程序中安装该模块,或者,反过来,该库应该提供一个 Injector 实例,您可以使用 @987654325 在您的应用程序中使用该实例@ 并提供特定于应用程序的模块。这种方法的轻微修改是将特定于应用程序的模块传递给库,因此它们将用于创建Injector。我最近在自定义的类 servlet 接口上编写了基于 Guice 的 API,该接口使用最后一种方法根本不支持任何类型的 DI,并且运行良好。

在 servlet 或 Jersey 环境中使用 Guice 一点也不难。例如,后者与 Guice 具有开箱即用的集成(至少在 1.x 版本中)。 Guiceservlet extension也很好很方便。试试看吧。

【讨论】:

  • 所以可以传递Injector 实例吗?我试图避免这种情况。
  • @AlexB 不不不。我不是那个意思。我想我没有说清楚,可能犯了一个错误。我已经更新了答案。
【解决方案2】:

static &lt;T&gt; Utils.getInstanceOf (Class&lt;T&gt; type);

你最终得到的是Service Locator

虽然在一些小情况下,injector 逃到其他创建对象中是可以接受的,但我不认为这是其中之一。您已经了解了服务定位器的所有缺点,并且可以通过使用您已经在使用的工具来获得所有优点。

【讨论】:

    【解决方案3】:

    如果您有一个方法需要在运行时创建类 C 的新实例,请将 Provider 绑定到您的类。 C将以通常的方式绑定,例如

    public CModule extends AbstractModule {
        @Overide
        public void configure() {
            bind(C.class).to(CImpl.class);
        }
    }
    

    创建 C 实例的类如下所示:

    class UserOfC {
        private Provider<C> cProvider;
        ...
    
        @Inject
        UserOfC(Provider<C> cProvider, ...) {
            this.cProvider = cProvider;
            ...
        }
    
        public void doSomethingWithAC (...) {
            C myC = cProvider.get();  // not a singleton; new instance created!
            ...
        }
    }
    

    Guice 支持免费提供 Provider 注入。如果 C 被绑定,您可以像注入 C 实例一样轻松地注入 Provider。

    其他建议:

    我强烈建议您在构建时注入所有依赖项,如果可能的话,即使它需要编写更多的代码行。我已经使用 Guice 多年,还没有需要部分构建或任何其他高级功能。

    当我面临部分注入的需求时,我一般会写自己的工厂。我发现编写代码时更容易理解和调试。

    【讨论】:

      【解决方案4】:

      使用注入器的“通常”模式是在项目的某个顶级入口点中设置它(在 servlet 场景中,使用 Guice-Servlet,这将是 GuiceServletContextListener)。为了模块化,您可能希望在某个依赖项的入口点配置一个单独的注入器,并使其负责连接该依赖项。如果您在依赖项中同时需要单独的绑定和来自父项目的绑定,那么您可以创建一个子注入器,如果没有找到绑定,则委托给它的父项目。 Guice 支持这一点。

      但是,您想在依赖项中设置一个注入器并在您的主应用程序中使用它,这对我来说似乎很奇怪。这意味着依赖项知道主应用程序所需的所有绑定。我不太确定你想用这种方法实现什么。是您的两个应用程序具有相同/非常相似的绑定设置并且您不想重复吗?在这种情况下,您应该定义一个包含所有绑定配置的模块一次(可能在依赖项中),并在每个应用程序的入口点设置注入器时使用它。就您的情况而言,就这么多。

      对于您的一般问题。我认为避免明确通过注射器是一种很好的做法。每当你这样做时,你就违背了将依赖注入作为一个透明项目的想法,并且你将自己绑定到一个具体的注入框架。在大多数情况下,您可以使用ProvidersFactories 避免显式引用注入器。

      【讨论】:

      • 谢谢!我主要尝试不重复代码,并且在共享库中还有一个默认模块,其中包含默认接口实现的绑定。主要的应用程序还没有提供自己的模块和绑定。所以在某种程度上,共享库知道其他应用程序中使用的所有默认绑定。
      • 您自己说“还”意味着将来可能会有单独的绑定。鉴于您的应用程序使用公共模块创建了自己的注入器,这非常好并且可以轻松实现。我看到注入器配置代码就像一个配置文件:更改它是完全可以的,它应该位于它配置的应用程序中——因为我会在那里搜索它——它应该在一个类中除了配置。
      【解决方案5】:

      是的,可以这样通过注射器。

      即使我们对我们的 wicket 应用程序做了类似的事情,所以对于非非wicket 页面,我们只是使用 injector.get.inject(this) 并传入构造函数。

      而且效果很好。

      希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-10
        • 1970-01-01
        • 2011-08-15
        • 1970-01-01
        相关资源
        最近更新 更多