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

2 java dependency-injection guice

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

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

public class EventMonitor {
    @Inject
    private EventBus guavaEventBus;

    // ...
}
Run Code Online (Sandbox Code Playgroud)

在我的应用程序的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.
    }
}
Run Code Online (Sandbox Code Playgroud)

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

ThreadFactory factory = ThreadManager.currentRequestThreadFactory();
Executor executor = Executors.newCachedThreadPool(factory)
EventBus eventBus = new AsyncEventBus(executor);
Run Code Online (Sandbox Code Playgroud)

我因为两个(看似不可注入的)静态方法ThreadManager而窒息Executors而且因为我的引用是针对a 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);
Run Code Online (Sandbox Code Playgroud)

所以我问:考虑到我想要构建我的方式EventBus,一个穿着战斗的Guice老手怎么会把这里的东西连接起来(使用ThreadFactory,ExecutorEventBus)这样,EventMonitor完全配置EventBus好的注入?我想,一旦我看到这个更"复杂"的例子,我就会开始通过树木看到森林.提前致谢.

Dan*_*tin 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • @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)
    }
    
    Run Code Online (Sandbox Code Playgroud)

    当你尝试创建注入器时,guice会抛出一个配置错误,因为你已经告诉过使用两种不同的方法来获取一个注入器Executor.现在,如果我有这样的事情:

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

    但仍然有providesAsyncEventBus方法如上,那么当你尝试创建喷油器,因为你没有告诉它如何创建吉斯将抛出一个错误ExecutorprovidesAsyncEventBus要求.你需要一个额外的行,如:

    bind(Executor.class).to(ExecutorService.class);
    
    Run Code Online (Sandbox Code Playgroud)

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

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

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