Guice:用复杂的创作图案连接豆子

时间:2013-02-03 02:41:09

标签: java dependency-injection guice

我正在使用Guava为发布 - 订阅消息传递服务创建EventBus。我也是第一次尝试使用Guice。我已经阅读了Guice教程,并且一直在使用AbstractModuleBinder类。但是,当我离开教程并尝试为我的项目实际工作时,我很窒息。

我的项目有一个EventMonitor,它将有一个Guice注入的Guava EventBus实例:

public class EventMonitor {
    @Inject
    private EventBus guavaEventBus;

    // ...
}

在我的应用程序的Guice / DI / Bootstrapping代码中,我定义了AbstractModule具体结果:

public class MyAppModule extends AbstractModule {
    @Override
    public void configure() {
        // Here is where I want to wire together the EventBus to give
        // to the EventMonitor.
    }
}

最终,我想要通常构建的EventBus(在非Guice代码中),如下所示:

ThreadFactory factory = ThreadManager.currentRequestThreadFactory();
Executor executor = Executors.newCachedThreadPool(factory)
EventBus eventBus = new AsyncEventBus(executor);

我因为ThreadManagerExecutors上的两个(看似不可注入的)静态方法而窒息,因为我的引用是EventBus,但实际对象是AsynEventBus;因此我不确定如何绑定它:

// Doesn't work because how does Guice know I'm referencing an AsyncEventBus?!?
bind(EventBus.class).toInstance(executor);

// Doesn't work because now I've lost the ability to pass the
// AsyncEventBus an 'executor' and no-arg ctor is used!
bind(EventBus.class).to(AsyncEventBus.class);

所以我问:考虑到我想要构建我的EventBus的方式,一个穿着战斗的Guice老兵怎么会在这里连线(使用ThreadFactory,{{1} }和Executor)这样在EventBus中,完全配置EventMonitor是否正确注入?我想,一旦我看到这个更“复杂”的例子,我就会开始通过树木看到森林。提前谢谢。

1 个答案:

答案 0 :(得分:6)

  一个穿着战斗的Guice老将怎么会把事情搞得一团糟

两个词:提供者方法。

public class MyAppModule extends AbstractModule {
    @Override
    public void configure() {
        bind(EventBus.class).to(AsyncEventBus.class);
    }

    @Provides @Singleton
    ThreadFactory providesThreadFactory() {
        return ThreadManager.currentRequestThreadFactory();
    }

    @Provides @Singleton
    Executor providesExecutor(ThreadFactory factory) {
        return Executors.newCachedThreadPool(factory)
    }

    @Provides @Singleton
    AsyncEventBus providesAsyncEventBus(Executor executor) {
        return new AsyncEventBus(executor);
    }
}

命名以“提供”开头的@Provides方法的惯例不是guice需要的,但是你真正想做的事情,特别是在大型代码库上。能够在代码体中搜索“提供”并找到提供特定对象的方法。

在评论中回答问题的一些注释:

  • Guice检查您为Module方法安装的每个@Provides实例,并安装它们,就好像您将方法的返回类型与.toProvider绑定到匿名{ {1}}实例。所以关于它们的好处是你在Provider方法中不需要任何额外的代码来让它们使用它们。

  • configure注释告诉guice您只需要该事物的一个实例,因此它只会调用@Singleton方法一次。默认情况下,如果您将其关闭,则每次需要注入实例时,都要让guice调用您的提供程序和/或实例化一个新对象(取决于您为该类配置的内容)。注意:有些人在第一次发现这个时就吓坏了,然后想把provides*放到各处“以提高效率” - 这是一种不正确的反应。实际上,您确实希望谨慎使用@Singleton,并且只有在有多个不同实例的情况下才会使用它。

    在这种情况下,我们希望确定周围只有一个@Singleton。只要您不直接将EventBusExecutor注入任何其他类,就可以将注释保留在这些方法之外。对于ThreadFactory,这几乎肯定不会有所作为,因为我认为ThreadFactory每次被调用时都会返回相同的实例。它会对ThreadManager.currentRequestThreadFactory()产生影响,但您可能想在其他地方使用新的Executor实例吗?

  • Executor方法的参数连接方式与配置的其余部分相同。例如,在这种情况下,我知道@Provides将获得providesAsyncEventBus返回的Executor,因为只要有providesExecutor,实例guice就会注入。{1}}如果我有两个这样的方法:

    Executor

    然后当您尝试创建注入器时,guice会抛出配置错误,因为您已经告诉过使用两种不同的方法来获取@Provides @Singleton Executor providesExecutor1(ThreadFactory factory) { return Executors.newCachedThreadPool(factory) } @Provides @Singleton Executor providesExecutor2(ThreadFactory factory) { return Executors.newScheduledThreadPool(10, factory) } 。现在,如果我有这样的事情:

    Executor

    但仍然使用上面的@Provides @Singleton ExecutorService providesExecutor1(ThreadFactory factory) { return Executors.newCachedThreadPool(factory) } @Provides @Singleton ScheduledExecutorService providesExecutor2(ThreadFactory factory) { return Executors.newScheduledThreadPool(10, factory) } 方法,然后guice会在您尝试创建注入器时抛出错误,因为您没有告诉它如何创建providesAsyncEventBus Executor需要。你需要一个额外的行,如:

    providesAsyncEventBus

    bind(Executor.class).to(ExecutorService.class); 方法中解决问题。

  • 对于configure方法,您有几个选择。我相信到目前为止最简单的方法是只需要将register参数添加到需要注册的对象的EventBus - 带注释的构造函数中,然后执行@Inject构造函数的最后一行。

    其他选项包括使用静态注入(您可以阅读,但我不建议),或者使用多个绑定器将evtBus.register(this)与适当的注释绑定,然后在创建{的相同启动代码中绑定Set<Object> {1}},遍历该集合以注册任何内容。 (第二种方法可以用一些很好的模式来完成,但在你了解更多关于更简单的guice使用模式之前我不会推荐它)