java - Guice:使用复杂的创建模式连接一个 bean

标签 java dependency-injection guice

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

我的项目有一个 EventMonitor,它将有一个 Guice 注入(inject)的 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 上的两个(看似不可注入(inject)的)静态方法让我感到窒息,因为我的引用是针对 EventBus 但实际对象是 AsynEventBus;因此我不确定如何绑定(bind)它:

// 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 老手将如何在这里连接东西(使用 ThreadFactoryExecutorEventBus),以便在 EventMonitor 中正确注入(inject)完全配置的 EventBus?我想一旦我看到这个更“复杂”的例子,我就会开始透过树木看到森林。提前致谢。

最佳答案

how would a battle-worn Guice veteran wire things up here

两个词:提供者方法。

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以“provides”开头的方法不是 guice 需要的,而是您真正想要做的,尤其是在大型代码库上。能够在代码主体中搜索“提供”并找到提供特定对象的方法。

回答评论中问题的几点说明:

  • Guice 检查每个 Module您为 @Provides 安装的实例方法,并安装它们,就像您将方法的返回类型与 .toProvider 绑定(bind)一样匿名Provider实例。所以关于它们的伟大之处在于你不需要在你的 configure 中添加任何额外的代码。让它使用它们的方法。

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

    在这种情况下,我们要非常确定只有一个 EventBus大约。只要你不直接注入(inject) ExecutorThreadFactory进入任何其他类,你可以离开这些方法的注释。对于 ThreadFactory这几乎肯定不会有什么不同,因为我认为 ThreadManager.currentRequestThreadFactory()无论如何,每次调用它时都会返回相同的实例。这会对 Executor 产生影响, 但也许你想要一个新的 Executor在您使用它的其他地方实例?

  • @Provides的参数方法的连接方式与其余配置相同。例如,在这种情况下,我知道 providesAsyncEventBus将得到 Executor providesExecutor 返回的因为这是 guice 将在需要 Executor 时注入(inject)的实例.如果我有两种这样的方法:

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

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

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

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

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

    在你的configure解决问题的方法。

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

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

关于java - Guice:使用复杂的创建模式连接一个 bean,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14668968/

相关文章:

java - 未定义 Base 类型的子方法

java - 字节数组到位图不显示Java Android

java - 使用 jax-ws 的 soap 请求中缺少前缀

angular - 注入(inject)器与 ViewContainerRef.injector 与 ViewContainerRef.parentInjector

configuration - Unity 应该在代码还是配置文件中配置?

java - Guice 绑定(bind) API 示例

java - Servlet 的 Google guice 注入(inject)器返回 500 错误

java - 如何使用 JPA 和 Hibernate 初始化数据库表(插入行)?

c# - ASP.NET Core 2 中多个相同类型实例的依赖注入(inject)

java - 如何根据部署实例改变常量