Guice和Swing';s的并发模型似乎不兼容

Guice和Swing';s的并发模型似乎不兼容,swing,concurrency,guice,Swing,Concurrency,Guice,这里是Guice 4.0。我已经使用Guice好几年了,非常喜欢它。我现在需要一个Swing应用程序,并且对如何设计Guice API来满足Swing(IMHO)奇怪的需求/最佳实践感到困惑 通常,当我使用Guice时,我有一个主“驱动程序”类,如MyApp,我用Guice连接它,如下所示: // Groovy pseudo-code class MyApp { @Inject FizzClient fizzClient @Inject BuzzClien bu

这里是Guice 4.0。我已经使用Guice好几年了,非常喜欢它。我现在需要一个Swing应用程序,并且对如何设计Guice API来满足Swing(IMHO)奇怪的需求/最佳实践感到困惑

通常,当我使用Guice时,我有一个主“驱动程序”类,如
MyApp
,我用Guice连接它,如下所示:

// Groovy pseudo-code
class MyApp {
    @Inject
    FizzClient fizzClient

    @Inject
    BuzzClien buzzClient

    static void main(String[] args) {
        MyApp myapp = Guice.createInjector(new MyAppModule()).getInstance(MyApp)
        myapp.run()
    }

    private void run() {
        // Do whatever the app does...
    }
}

class MyAppModule extends AbstractModule {
    @Override
    void configure() {
        bind(FizzClient).to(DefaultFizzClient)
        bind(BuzzClient).to(DefaultBuzzClient)
        // etc...
    }
}
但是在Swing的情况下,
JFrame
是GUI应用程序,因此对我来说,让
MyApp
扩展
JFrame
是有意义的:

class MyApp extends JFrame {
    // etc...
}
问题在于,在Swing中,您需要进行一些非常规的线程欺骗,以便:

  • 任何操作UI的
    JFrame
    或其他
    JComponents
    的代码都需要发生在事件调度线程(EDT)上-这是通过
    SwingUtilities.invokeLater
    完成的;及
  • 任何会阻塞并需要一段时间才能完成的代码都需要被虹吸到它自己的
    SwingWorker
    线程中
  • 因此,假设我需要在应用程序启动时预热一个大型缓存(可能需要30-60秒)。以下是我迄今为止最好的尝试:

    @Canonical // Creates a tuple constructor and a bunch of other stuff for me
    @Slf4j
    class MyApp extends JFrame {
        @Inject
        MyAppCache appCache
    
        static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                void run() {
                    // The Guice-based bootstrapping has to happen inside invokeLater
                    // because MyApp *is* a JFrame (hence UI).
                    Guice.createInjector(new MyAppModule()).getInsance(MyApp).init()
                }  
            })
        }
    
        void init() {
            new SwingWorker<Void,String>() {
                @Override
                protected Void doInBackground() {
                    appCache.warmUp()
                    publish('Cached is warmed up!')
                }
    
                @Override
                protected void process(List<String> updates) {
                    log.info(updates)
                }
            }.execute()
        }
    }
    
    class MyAppModule extends AbstractModule {
        @Override
        void configure() {
            bind(MyAppCache).to(DefaultAppCache)
        }
    
        @Provides
        MyApp providesMyApp(MyAppCache appCache) {
            MyApp app = new MyApp(appCache)
            app.title = 'My App'
            app.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
            app.pack()
            // ...etc. (configure the parent JFrame)
        }
    }
    
    @Canonical//为我创建了一个元组构造函数和一堆其他东西
    @Slf4j
    类MyApp扩展了JFrame{
    @注入
    MyAppCache appCache
    静态void main(字符串[]参数){
    SwingUtilities.invokeLater(新的Runnable(){
    @凌驾
    无效运行(){
    //基于Guice的引导必须在invokeLater内部进行
    //因为MyApp*是一个JFrame(因此是UI)。
    createInjector(新的MyAppModule()).getInsance(MyApp.init())
    }  
    })
    }
    void init(){
    新SwingWorker(){
    @凌驾
    受保护的Void doInBackground(){
    appCache.warmUp()
    发布('缓存已预热!')
    }
    @凌驾
    受保护的无效进程(列表更新){
    日志信息(更新)
    }
    }.execute()
    }
    }
    类MyAppModule扩展了AbstractModule{
    @凌驾
    void configure(){
    绑定(MyAppCache).到(DefaultAppCache)
    }
    @提供
    MyApp提供MyApp(MyAppCache appCache){
    MyApp app=新的MyApp(appCache)
    app.title='我的应用'
    app.defaultCloseOperation=JFrame.EXIT\u ON\u CLOSE
    app.pack()
    //…等(配置父JFrame)
    }
    }
    
    当我运行此代码时,我在
    SwingWorker
    中的
    appCache.warmUp()
    语句中得到一个NPE。这是因为上述代码实际上涉及3个线程:

    • main-其中
      main
      init
      正在运行
    • EDT-其中Guice正在引导
      MyApp
      实例
    • Swing Worker-Swing Worker正在运行并试图预热缓存的位置
    因此,当我们执行Swing worker时,Guice还没有机会引导
    MyApp
    实例(因此,它的缓存属性)

    我有什么想法(带有特定的代码示例)如何让GUI与Swing的并发模型正常工作?

    我不熟悉,但在调用
    EventQueue.invokeLater()后,您可以继续任何非GUI初始化-这样的代码只需在上运行即可。可以看到一个例子。当然,您必须同步对任何共享数据的访问,但是您可以在初始线程结束时以通常的方式使用
    EventQueue.invokeLater()
    更新GUI。在此期间,GUI应防止使用任何不完整的数据

    顺便说一句,我认为没有理由
    MyApp
    扩展
    JFrame
    。相反,
    add()
    将封闭的
    容器
    添加到
    JFrame
    的实例中。这里列举了几个例子。

    我不熟悉,但是您可以在调用
    EventQueue.invokeLater()
    之后继续任何非GUI初始化-这样的代码只需在上运行即可。可以看到一个例子。当然,您必须同步对任何共享数据的访问,但是您可以在初始线程结束时以通常的方式使用
    EventQueue.invokeLater()
    更新GUI。在此期间,GUI应防止使用任何不完整的数据


    顺便说一句,我认为没有理由
    MyApp
    扩展
    JFrame
    。相反,
    add()
    将封闭的
    容器
    添加到
    JFrame
    的实例中。引用了几个示例。

    您正在传递给
    invokeLater
    的Runnable中调用
    init
    自己。为什么它会在主线程而不是EDT线程上运行?我真的很想了解一个非单线程的GUI库。谢谢@JeffBowman(+1)-因为
    SwingWorker
    有一些魔力-自己运行此代码,你会看到工作代码在自己的线程中执行,独立于主要和EDT。我有兴趣看到完整的stacktrace。在执行injector.getInstance(MyApp.class).init()时,MyApp已准备就绪,因此完全注入。您的代码中有一个输入错误(“GetInAsance”):我假设我在这里看到的代码不是您执行的代码,错误可能不在这里。您正在调用传递给
    invokeLater
    的Runnable中的
    init
    。为什么它会在主线程而不是EDT线程上运行?我真的很想了解一个非单线程的GUI库。谢谢@JeffBowman(+1)-因为
    SwingWorker
    有一些魔力-自己运行此代码,你会看到工作代码在自己的线程中执行,独立于主要和EDT。我有兴趣看到完整的stacktrace。在执行injector.getInstance(MyApp.class).init()时,MyApp已准备就绪,因此完全注入。您的代码中有一个输入错误(“GetInasence”):我假设代码