Swing UI弹簧启动
我想在Spring-Boot应用程序中为我的Swing UI组件使用依赖注入,并且很难弄清楚如何在Event Dispatch Thread上正确执行UI行为。Swing UI弹簧启动
我想出什么样的主意首先是这样的:
应用
@SpringBootApplication
public class App {
private static AppView view;
@Bean
public AppView appView() {
return view;
}
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(() -> view = new AppView());
SpringApplication app = new SpringApplication(App.class);
app.run(args);
}
}
APPVIEW
public class AppView extends JFrame {
...
@Inject
private DependencyWithTimeConsumingOperations backendController;
@Inject
private JPanel someChildComponent;
@PostConstruct
public void init() {
constructView(); // inits frame properties and child components
showView();
}
private void showView() {
SwingUtilities.invokeLater(() -> {
pack();
setVisible(true);
});
}
...
}
当某些UI事件发生后端依赖被调用。我观察到的是,后端调用在EDT上执行而不是主应用程序线程,我认为这很糟糕。据我所知,Swing没有太多经验,只有在UI上执行UI更新。
有没有更好的方法来连接我的依赖关系,以便一切都在其正确的线程中执行?到目前为止我能发现的东西似乎有点过时,或者我明白不明白答案:-)
不知道它在这么长时间后是否仍然与你有关:)但是因为它可以帮助别人,我会尽力回答。
Spring只是注入对象,它不管理线程。如果您手动实例化并设置了后端控制器,则这种行为将是相同的,这意味着EDT(或调用操作的任何线程)将是在控制器上执行代码的那个。
如果您明确地想要在不同的线程中运行,那么我们需要更多地了解控制器中的方法。他们的方法是你想打电话而不是等待答复(火和忘记)?或者,也许你需要答复,但可以同时运行多个答案?在这些情况下,您可以采取Executors类的优势,做这样的事情:
Executors.newSingleThreadExecutor().execute(() -> backendController.timeConsumingOperation1()); // Fire and forget. The operation timeConsumingOperation1 will be executed by a separate thread and the EDT will continue to the next line (won't freeze your GUI)
如果您需要的结果,您可以将其提交给游泳池和轮询的结果(也许用“刷新”按钮屏幕)。请记住,只要调用“get()”,当前线程将在继续下一行之前等待池线程完成。
Future result = Executors.newSingleThreadExecutor().execute(() -> backendController.timeConsumingOperation2);
result.isDone(); // You can add a "refresh" button or a scheduled task to check the state...
doSomething(result.get()); // This will hold the current thread until there is a response from the thread running the timeConsumingOperation
或者,也许你想,直到你有一个从全称为控制器方法的响应冻结GUI,但它们可以被安全地调用并行:
ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Future<Object>> results = executorService.invokeAll(
Arrays.asList(() -> backendController.timeConsumingOp3(),() -> backendController.timeConsumingOp4));
results.forEach(e -> doSomething(e.get())); // The tasks will be executed in parallel and "doSomething()" will be called as soon as the result for the given index is available
executorService.shutdown(); // Always shutdown
当然这只是一个例子,但在大型Swing应用程序中,创建线程池(由控制器共享)是我们提交长时间运行任务的良好习惯。您可以根据核心数量(Runtime.getRuntime().availableProcessors()
)配置池大小以最好地利用计算机上可用的资源(所提交的任务将不受限制地排队,但只有X个线程将并行执行任务,其中X是池大小)。
该项目实际上已经睡前不久。但是既然你指出了我的'Denkfehler',我接受你的答案。谢谢。 – nansen