lam*_*noy 8 java dependency-injection guice
我是Google Guice的新手并且从概念上理解依赖注入,但是在尝试将其合并到我的应用程序中时遇到了问题.我的具体问题是关于Singleton对象.这是一个例子:
首先,我的Module类,它将重的Singleton Connection接口绑定到它的实现.
public class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(Connection.class).to(MyConnection.class).asEagerSingleton();
}
}
Run Code Online (Sandbox Code Playgroud)
现在,在我的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...
}
}
Run Code Online (Sandbox Code Playgroud)
到目前为止一切都很好......现在,我有一些利用我的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));
}
}
Run Code Online (Sandbox Code Playgroud)
我的第一个假设是我只是添加@Inject,cxn但这不起作用,因为我没有从Guice获得实例; 它只是给了我一个NPE.我看到它的方式,我有2个选项来获取Connection对象:
getConnection()基本上遵循服务定位器模式在MyApplication中公开一个方法requestStaticInjection(MyConfiguration)到MyModule我选择了#2,但文档说:
建议不要将此API用于一般用途
将Singleton提供给需要它的类而不必Injector.getInstance每次都要经历的最佳实践是什么?我错过了什么?
Dan*_*den 13
你错误地考虑了依赖注入.依赖注入和服务定位器是彼此的镜像:使用服务定位器,您可以询问它是否为对象.使用依赖注入,你不会去寻找依赖,它们只是交给你.
基本上,"它一直是乌龟"!应该注入你班级的每一个依赖.如果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;
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
您可以在单元测试中采用类似的方法:
/** 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
}
}
Run Code Online (Sandbox Code Playgroud)
依赖注入还鼓励我强烈推荐的另一种最佳实践,即设计类型系统,使无效状态不可表示(尽可能最大程度).如果MyApplication在其构造函数中传递了所有配置需求,则不可能有一个MyApplication没有有效配置的对象.这允许您"前加载"类不变量,这使得更容易推理对象的行为.
最后,说明一下Injector.getInstance().理想情况下,您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();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
24958 次 |
| 最近记录: |