Guice和Swing';s的并发模型似乎不兼容
这里是Guice 4.0。我已经使用Guice好几年了,非常喜欢它。我现在需要一个Swing应用程序,并且对如何设计Guice API来满足Swing(IMHO)奇怪的需求/最佳实践感到困惑 通常,当我使用Guice时,我有一个主“驱动程序”类,如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
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中,您需要进行一些非常规的线程欺骗,以便:
JFrame
或其他JComponents
的代码都需要发生在事件调度线程(EDT)上-这是通过SwingUtilities.invokeLater
完成的;及SwingWorker
线程中@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正在运行并试图预热缓存的位置
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”):我假设代码