当前位置: 首页 > 知识库问答 >
问题:

Guice单例静态注入模式

臧亦
2023-03-14

我是Google Guice的新手,从概念上理解依赖注入,但在尝试将其并入我的应用程序时遇到了一些问题。我的具体问题是关于单例对象的。这里有一个例子:

首先是我的模块类,它将一个沉重的单例连接接口绑定到它的实现。

html" target="_blank">public class MyModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(Connection.class).to(MyConnection.class).asEagerSingleton();
    }
}

现在,在我的main方法中,我实例化我的应用程序服务器并注入连接:

public class MyApplication {
    @Inject
    public MyApplication(Connection cxn) {

    }

    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new MyModule());
        MyApplication app = injector.getInstance(MyApplication.class);
        // Start application, add ShutdownHook, etc...
    }
}

到目前为止一切顺利...现在,我有一些利用连接对象的DAO类,但使用静态方法检索,如下所示:

public class MyConfiguration {
    private Config conf;
    private Connection cxn; // Would like to have this injected

    private MyConfiguration(Config conf) {
        this.conf = conf;
    }

    public static MyConfiguration getConfig(String name) {
        return new MyConfiguration(cxn.getConfig(name));
    }
}

我的第一个假设是,我只需将@inject添加到cxn中,但这不起作用,因为我不是从Guice获取实例;只是给了我一个NPE。在我看来,我有两个选择来获取连接对象:

  1. 在MyApplication中公开getConnection()方法,该方法基本上遵循服务定位器模式
  2. RequestStaticInjection(MyConfiguration)添加到MyModule

我选择了第2条,但文件上说:

此API不推荐用于一般用途

将我的单例提供给需要它的类的最佳实践是什么,而不必每次都经过injector.getinstance?我错过了什么?

共有1个答案

谷隐水
2023-03-14

你对依赖注入的想法是错误的。依赖注入和服务定位器是彼此的镜像:使用服务定位器,您向它请求一个对象。使用依赖项注入,您不会去寻找依赖项,它们只是交给您。

基本上“一路下来都是乌龟”!您的类拥有的每个依赖项都应该被注入。如果MyApplication需要MyConfiguration对象,它应该只接受MyConfiguration对象作为构造函数参数,而不必担心它是如何构造的。

现在,这并不是说您永远不能手动使用new--但是您应该为没有外部依赖关系的值类型对象保留这一点。(在这些情况下,我认为使用静态工厂方法通常比使用公共构造函数更好,但这不是重点。)

现在有几种方法可以做到这一点。一种方法是将myConfiguration分割成许多小块,这样就可以执行myConfiguration.getConfig(“x”)或类似的操作,而不是执行@inject@configuration(“x”)string。或者,您可以使myconfiguration本身可注入,然后为这些片段提供访问器方法。正确的答案在某种程度上取决于您试图建模的数据类型--使依赖关系过于细粒度,您的绑定可能会变得难以维护(尽管有一些方法可以使其更好);使依赖关系过于粗略,从而使其更难测试(例如:哪个更容易,只提供您测试的类所需的“X”配置,还是构建整个应用程序的配置?)。

你甚至可以同时做两件事:

/** Annotates a configuration value. */
@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
public @interface Config {
  String value();
}

/** Installs bindings for {@link MyConfiguration}. */
final class MyConfigurationModule extends AbstractModule {
  @Override protected void configure() {}

  @Provides
  @Singleton
  MyConfiguration provideMyConfiguration() {
    // read MyConfiguration from disk or somewhere
  }

  @Provides
  @Config("x")
  String provideX(MyConfiguration config) {
    return config.getConfig("x").getName();
  }
}

// elsewhere:

/** The main application. */
final class MyApplication {
  private final String xConfig;

  @Inject MyApplication(@Config("x") String xConfig) {
    this.xConfig = xConfig;
  }

  // ...
}

您可以在单元测试中采用类似的方法:

/** Tests for {@link MyApplication}. */
@RunWith(JUnit4.class)
public final class MyApplicationTest {
  // Note that we don't need to construct a full MyConfiguration object here
  // since we're providing our own binding, not using MyConfigurationModule.
  // Instead, we just bind the pieces that we need for this test.
  @Bind @Config("x") String xConfig = "x-configuration-for-test";

  @Before public void setUp() {
    // See https://github.com/google/guice/wiki/BoundFields
    Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
  }

  @Inject MyApplication app;

  @Test public void testMyApp() {
    // test app here
  }
}

依赖注入还鼓励了另一个我强烈推荐的最佳实践,即设计您的类型系统,使无效状态不可表示(最大可能的程度)。如果MyApplication需要的所有配置都在其构造函数中传递,那么就不可能有一个没有有效配置的MyApplication对象。这允许您“前置加载”您的类不变量,这使得对您的对象的行为进行推理变得容易得多。

最后是关于injector.getInstance()的说明。理想情况下,您可以在程序中使用injector一次:在程序构造完成后立即使用injector。也就是说,您应该能够执行guice.createinjector(...).getInstance(MyApplication.Class).start(),并且永远不要在任何地方存储对injector的引用。我倾向于使用Guava的serviceManager抽象构建应用程序(请参见本问题),因此我需要做的唯一事情是:

public static void main(String[] args) throws Exception {
  Injector injector = Guice.createInjector(...);
  ServiceManager manager = injector.getInstance(ServiceManager.class);
  manager.startAsync().awaitHealthy();
}
 类似资料:
  • 问题内容: 我想知道用google guice注入实用程序方法是否是一种好的样式。 假设我们有一个Converter Utility类: 我的想法是使用guice将这个Utility像Singleton这样注入 建议使用guice构建的应用程序采用哪种方式? 问题答案: 这取决于您的方法的性质。 如果有的话 简单 确定性的(即不依赖于其他参数) 没有副作用 不太可能改变 等等 您可以将其保留为静态

  • 这个例子取自一本关于依赖注入的书。 在第87页,有这个例子。 我理解代码的用途,但我不理解的是如何声明或配置我的,以便我可以创建这个类的实例。 似乎注入了< code > deliver factory 。这个例子没有说是如何实现的,但是让我们承认它是通过构造函数注入实现的。在这种情况下,如何传递<代码>列表 我不明白的是,如何在构造函数的签名中同时自动注入对象和实例特定对象。在这种情况下,我不知

  • 我是Guice注入的新手。如何将类注入到将在静态方法中使用的静态变量中? 这是我想在 doLocalize() 方法中使用注入的变量 b 的类。 这是我想要对其执行guice注入的模块类。 注意:我不能改变我的模块和B类,因为它来自另一个依赖项。 我想在我的A类中注入的类

  • 我们的项目设置如下: 1)主模块:包含一个启动Spark流服务器的驱动程序。它有自己的Guice注入器。 2)当消息进来时,它进入另一个模块,该模块创建它自己的Guice注入器。 3) 此模块使用其他模块中的类,这些模块本身使用依赖模块。这些模块中的每一个都创建了自己的Guice注入器,因此可以独立工作、单独测试等。 这里有一个问题:现在我们需要一个单例,但是作为@Singleton创建的对象绑定

  • 问题内容: 我有一个需要一些模块。有没有办法可以注入模块本身?我意识到这有点麻烦。 例: 我想在这种情况下,解决方案是将方法转换为完整的类。这显然是一个简化的示例;我正在处理的代码有很多这样的方法,因此将它们分成单独的类并引入配置它们的模块会增加相当多的混乱- 我认为Guice就是要减少样板混乱? 也许这反映了我对Guice的相对呆板,但是我遇到了很多尝试着做上述事情的案例。我肯定错过了什么… 问

  • 我正在做一个使用依赖注入GoogleGuice框架的项目。 可以将类与singleton作用域绑定在一起,如下所示: 如果类本身是一个真正的单例类: 或者 因此,可以在项目中声明两个单例,第一个由Guice声明,第二个由传统方式声明。 Google Guice还可以使用方法将Interface绑定到特定实例。 因此,在Java中,用以下声明绑定Singleton,而不是绑定Singleton作用域