[英]Spring-Boot with Swing UI
我想对 Spring-Boot 应用程序中的 Swing UI 组件使用依赖注入,并且很难弄清楚如何在事件调度线程上正确执行 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);
}
}
应用视图
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 没有太多经验的是,只应在 EDT 上执行 UI 更新。
有没有更好的方法来连接我的依赖项,以便一切都在正确的线程中执行? 到目前为止我能找到的东西似乎有点过时,或者我显然不明白答案:-)
不确定这么长时间后它是否仍然与您相关:),但由于它可能对其他人有帮助,我会尝试回答。
Spring 只是注入对象,而不是管理线程。 如果您手动实例化和设置 backendController,行为将是相同的,这意味着 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 是池大小)。
只需使用代码
SpringApplicationBuilder(Main.class).headless(false).run(args);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.