Spring-使用 Swing 启动 UI
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);
}
}
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 更新应该在 EDT 上执行。
是否有更好的方法来连接我的依赖项,以便所有内容都在其正确的线程中执行?到目前为止我能找到的东西似乎有点过时或者我显然不明白答案:-)
不知道过了这么久它是否仍然与你相关:),但因为它可能对其他人有帮助,所以我会尽力回答。
Spring 只注入对象,不管理线程。如果您手动实例化并设置 backendController,行为将是相同的,这意味着 EDT(或调用该操作的任何线程)将是在控制器上执行代码的那个。
如果您明确想要 运行 在不同的线程中,我们需要了解更多关于控制器中方法的信息。它们是您想要调用而不是等待回复(即发即忘)的方法吗?或者您可能需要回复,但可以同时 运行 多个回复吗?在这些情况下,您可以利用 Executors class 并执行以下操作:
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)
如果您需要结果,您可以将其提交到池中并轮询结果(可能使用屏幕上的 "refresh" 按钮)。请记住,一旦您调用 "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 应用程序中,创建线程池(由控制器共享)是一种很好的做法,我们可以向其提交长 运行ning 任务。您可以根据核心数 (Runtime.getRuntime().availableProcessors()
) 配置池大小,以最好地利用机器上可用的资源(提交的任务将不受限制地排队,但只有 X 线程会并行执行任务,其中 X 是池大小)。
只需使用代码
SpringApplicationBuilder(Main.class).headless(false).运行(args);
我想在 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);
}
}
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 更新应该在 EDT 上执行。
是否有更好的方法来连接我的依赖项,以便所有内容都在其正确的线程中执行?到目前为止我能找到的东西似乎有点过时或者我显然不明白答案:-)
不知道过了这么久它是否仍然与你相关:),但因为它可能对其他人有帮助,所以我会尽力回答。
Spring 只注入对象,不管理线程。如果您手动实例化并设置 backendController,行为将是相同的,这意味着 EDT(或调用该操作的任何线程)将是在控制器上执行代码的那个。
如果您明确想要 运行 在不同的线程中,我们需要了解更多关于控制器中方法的信息。它们是您想要调用而不是等待回复(即发即忘)的方法吗?或者您可能需要回复,但可以同时 运行 多个回复吗?在这些情况下,您可以利用 Executors class 并执行以下操作:
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)
如果您需要结果,您可以将其提交到池中并轮询结果(可能使用屏幕上的 "refresh" 按钮)。请记住,一旦您调用 "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 应用程序中,创建线程池(由控制器共享)是一种很好的做法,我们可以向其提交长 运行ning 任务。您可以根据核心数 (Runtime.getRuntime().availableProcessors()
) 配置池大小,以最好地利用机器上可用的资源(提交的任务将不受限制地排队,但只有 X 线程会并行执行任务,其中 X 是池大小)。
只需使用代码
SpringApplicationBuilder(Main.class).headless(false).运行(args);