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