2

我正在使用 GuavaEventBus为发布-订阅消息服务创建一个。我也是第一次尝试使用 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);

ThreadManager由于 and 上的两个(看似不可注入的)静态方法,我感到窒息Executors,并且因为我的参考是针对 anEventBus但实际对象是 an 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);

所以我问:考虑到我想要构建 my 的方式EventBus,一个久经沙场的 Guice 老兵如何将这里的东西连接起来(使用ThreadFactoryExecutorEventBus),以便在EventMonitor完全配置EventBus的情况下正确注入?我想一旦我看到这个更“复杂”的例子,我就会开始透过树木看到森林。提前致谢。

4

1 回答 1

6

一个久经沙场的古斯老兵怎么会把事情传到这里来?

两个词:提供者方法。

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到匿名Provider实例一样。所以关于它们的好处是你不需要在你的configure方法中添加任何额外的代码来让它使用它们。

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

    在这种情况下,我们要非常确定周围只有一个EventBus。只要您不直接注入ExecutorThreadFactory注入任何其他类,就可以将注释从这些方法中删除。因为ThreadFactory这几乎肯定不会有什么不同,因为我认为ThreadManager.currentRequestThreadFactory()每次调用它时都会返回相同的实例。Executor它会对Executor.

  • 方法的参数的@Provides连接方式与配置的其余部分相同。例如,在这种情况下,我知道这providesAsyncEventBus将得到Executor返回的 ,providesExecutor因为这是实例 guice 将在被要求提供Executor. 如果我有两种这样的方法:

    @Provides @Singleton
    Executor providesExecutor1(ThreadFactory factory) {
        return Executors.newCachedThreadPool(factory)
    }
    
    @Provides @Singleton
    Executor providesExecutor2(ThreadFactory factory) {
        return Executors.newScheduledThreadPool(10, factory)
    }
    

    然后,当您尝试创建注入器时,guice 会抛出配置错误,因为您已经告诉 guice 使用两种不同的方式来获取Executor. 现在,如果我有这样的东西:

    @Provides @Singleton
    ExecutorService providesExecutor1(ThreadFactory factory) {
        return Executors.newCachedThreadPool(factory)
    }
    
    @Provides @Singleton
    ScheduledExecutorService providesExecutor2(ThreadFactory factory) {
        return Executors.newScheduledThreadPool(10, factory)
    }
    

    但是仍然有providesAsyncEventBus上面的方法,那么当你尝试创建注入器时,guice 会抛出一个错误,因为你没有告诉它如何创建Executor需要providesAsyncEventBus的。您需要额外的一行,例如:

    bind(Executor.class).to(ExecutorService.class);
    

    在您configure解决问题的方法中。

  • 至于你的register方法,你有几个选择。我认为到目前为止最简单的方法是向需要注册的对象EventBus的 -annotated 构造函数添加一个参数,然后作为构造函数的最后一行。@InjectevtBus.register(this)

    其他选项包括使用静态注入(您可以阅读,但我不推荐),或Set<Object>使用 multibinder 将 a 与适当的注释绑定,然后在您创建 的同一启动代码中Injector,迭代该集合以注册任何内容。(第二种方法可以用一些不错的模式完成,但在您了解更多关于更简单的 guice 使用模式之前,我不会推荐它)

于 2013-02-03T03:05:31.893 回答