繁体   English   中英

带有 Swing UI 的 Spring-Boot

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM