繁体   English   中英

Guice Singleton静态注射模式

[英]Guice Singleton Static Injection Pattern

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

首先,我的Module类,它将重的Singleton Connection接口绑定到它的实现。

public class MyModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(Connection.class).to(MyConnection.class).asEagerSingleton();
    }
}

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

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...
    }
}

到目前为止一切都很好......现在,我有一些利用我的Connection对象的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。 我看到它的方式,我有2个选项来获取Connection对象:

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

我选择了#2,但文档说

建议不要将此API用于一般用途

将Singleton提供给需要它的类而不必每次都通过Injector.getInstance是什么最佳做法? 我错过了什么?

你错误地考虑了依赖注入。 依赖注入和服务定位器是彼此的镜像:使用服务定位器, 您可以询问它是否为对象。 使用依赖注入,你不会去寻找依赖, 它们只是交给你

基本上,“它一直是乌龟”! 应该注入你班级的每一个依赖。 如果MyApplication需要MyConfiguration对象,它应该只接受MyConfiguration对象作为构造函数参数,而不用担心它是如何构造的。

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

现在有几种方法可以做到这一点。 一种方法是将MyConfiguration分成许多小块,这样你就可以执行@Inject @Configuration("x") String或类似的东西,而不是做myConfiguration.getConfig("x") 或者,您可以使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();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM