Java Guice:用复杂的创作模式连接bean

Java Guice:用复杂的创作模式连接bean,java,dependency-injection,guice,Java,Dependency Injection,Guice,我正在使用Guava为发布-订阅消息服务创建一个EventBus。我也第一次尝试使用Guice。我已经阅读了Guice教程,并且一直在使用AbstractModule和Binder类。但是,当我离开教程并试图为我的项目做点什么的时候,我就窒息了 我的项目有一个EventMonitor,它将有一个Guice注入的GuavaEventBus实例: public class EventMonitor { @Inject private EventBus guavaEventBus;

我正在使用Guava为发布-订阅消息服务创建一个
EventBus
。我也第一次尝试使用Guice。我已经阅读了Guice教程,并且一直在使用
AbstractModule
Binder
类。但是,当我离开教程并试图为我的项目做点什么的时候,我就窒息了

我的项目有一个
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代码中):

我之所以窒息,是因为
ThreadManager
Executors
上有两个(似乎是不可注入的)静态方法,因为我的参考是
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
Executor
EventBus
),以便在
EventMonitor
中正确地注入完全配置的
EventBus
?我想,一旦我看到这个更“复杂”的例子,我就会开始透过树木看到森林。提前谢谢

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

回答评论中问题的几个注意事项:

  • Guice检查您为
    @安装的每个
    模块
    实例,提供
    方法,并将其安装为将方法的返回类型与
    .toProvider
    绑定到匿名
    提供程序
    实例。因此,它们的优点在于,在
    configure
    方法中不需要任何额外的代码就可以使用它们

  • @Singleton
    注释告诉guice您只需要该对象的一个实例,因此它只调用
    提供的*
    方法一次。默认情况下,如果您不使用它,则每次需要注入实例时,guice都会调用您的提供程序和/或实例化一个新对象(取决于您为该类配置的内容)。注意:有些人在第一次发现这一点时会发疯,然后想把
    @Singleton
    放在任何地方“为了效率”——这是一种不恰当的反应。实际上,您确实希望节省地使用
    @Singleton
    ,并且只在有多个不同实例的情况下使用

    在本例中,我们希望确保只有一个
    EventBus
    。只要不直接将
    Executor
    ThreadFactory
    注入任何其他类,就可以不使用这些方法。对于
    ThreadFactory
    来说,这几乎肯定不会有什么不同,因为我认为
    ThreadManager.currentRequestThreadFactory()
    每次调用时都返回相同的实例。这将对
    Executor
    产生影响,但也许您希望在其他使用它的地方有一个新的
    Executor
    实例

  • @提供的
    方法的参数与其余配置的连接方式相同。例如,在本例中,我知道
    providesacynceventbus
    将获得由
    providesecutor
    返回的
    Executor
    ,因为每当请求
    Executor
    时,实例guice都将注入该实例。如果我有两种这样的方法:

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

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

    bind(Executor.class).to(ExecutorService.class);
    
    中,配置
    方法以解决问题

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

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

谢谢@Daniel Martin(+1)-3个快速跟进:Singleton的经验法则是什么?提供程序是否应始终提供程序
@Singleton
?如果我没有在所有这些提供者上指定它,会发生什么?其次,
EventBus
对象上有一个
register(object)
方法,我在其中注册所有不同的“事件侦听器”。我在哪里进行这些
register
调用?我假设,如果我想要相同的,singleton
甚至