0

我想在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更新。

有没有更好的方法来连接我的依赖关系,以便一切都在其正确的线程中执行?到目前为止我能发现的东西似乎有点过时,或者我明白不明白答案:-)

回答

2

不知道它在这么长时间后是否仍然与你有关:)但是因为它可以帮助别人,我会尽力回答。

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是池大小)。

+0

该项目实际上已经睡前不久。但是既然你指出了我的'Denkfehler',我接受你的答案。谢谢。 – nansen

0

只需使用代码

SpringApplicationBuilder(Main.class).headless(假)。运行(参数);

+2

哪里?它有什么作用?它为什么解决这个问题? – JJJ