2015-03-02 115 views
3

我已經通過了很多關於將Spring DI與Jav​​aFx集成的教程,但是我碰到了一個簡單示例不包含的牆(我無法弄清楚)。JavaFX fxml - 如何使用嵌套自定義控件的Spring DI?

我想在視圖和表示層之間進行乾淨的分離。我想使用fxml來定義可組合的視圖,並使用Spring將它們連接在一起。這裏有一個具體的例子:

Dashboard.fxml:

<GridPane fx:id="view" 
      fx:controller="com.scrub.presenters.DashboardPresenter" 
      xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml"> 
    <children> 
     <TransactionHistoryPresenter fx:id="transactionHistory" /> 
    </children> 
</GridPane> 

Main.java:

public void start(Stage primaryStage) throws Exception{ 
    try { 
     AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppFactory.class); 
     SpringFxmlLoader loader = context.getBean(SpringFxmlLoader.class); 
     primaryStage.setScene(new Scene((Parent)loader.load("/views/dashboard.fxml"))); 
     primaryStage.setTitle("Hello World"); 
     primaryStage.show(); 
    } catch(Exception e) { 
     e.printStackTrace(); 
    } 
} 

SpringFxmlLoader.java:

public class SpringFxmlLoader { 

    @Autowired 
    ApplicationContext context; 

    public Object load(String url) { 
     try { 
      FXMLLoader loader = new FXMLLoader(getClass().getResource(url)); 
      loader.setControllerFactory(new Callback<Class<?>, Object>() { 
       @Override 
       public Object call(Class<?> aClass) { 
        return context.getBean(aClass); 
       } 
      }); 
      return loader.load(); 
     } catch(Exception e) { 
      e.printStackTrace(); 
      throw new RuntimeException(String.format("Failed to load FXML file '%s'", url)); 
     } 
    } 
} 

所以當DashboardPresenter被加載的SpringFxmlLoader正確地注入控制器w使用loader.setControllerFactory。

但是,自定義TransactionHistoryPresenter控件加載了新實例,而不是從spring上下文中加載。它必須使用它自己的FXMLLoader?

任何想法如何使自定義控件與春天很好玩?我真的不想讓控制器/演示者手動將它們連接起來。

+0

不知道(至少有一些)您的TransactionHistoryPresenter類。通常我會想到JavaFX將「控制器」稱爲演示者。如果你使用''來加載fxml,並讓主持人與之關聯,那麼控制器工廠將傳播到'',並且演示者變成一個Spring管理的bean。 – 2015-03-02 07:01:37

回答

4

這裏的主要問題是確保Spring在JavaFX應用程序的同一線程上初始化。這通常意味着Spring代碼必須在JavaFX應用程序線程上執行;其他耗時的工作當然可以在他們自己的線程上執行。

這是我放在一起使用this tutorial的解決方案和我自己的Spring Boot知識:

@SpringBootApplication 
@ImportResource("classpath:root-context.xml") 
public class JavaFXSpringApplication extends Application { 

    private static final Logger log = LoggerFactory.getLogger(JavaFXSpringApplication.class); 

    private Messages messages; 

    private static String[] args; 

    @Override 
    public void start(final Stage primaryStage) { 

    // Bootstrap Spring context here. 
    ApplicationContext context = SpringApplication.run(JavaFXSpringApplication.class, args); 
    messages = context.getBean(Messages.class); 
    MainPaneController mainPaneController = context.getBean(MainPaneController.class); 

    // Create a Scene 
    Scene scene = new Scene((Parent) mainPaneController.getRoot()); 
    scene.getStylesheets().add(getClass().getResource("/css/application.css").toExternalForm()); 

    // Set the scene on the primary stage 
    primaryStage.setScene(scene); 
    // Any other shenanigans on the primary stage... 
    primaryStage.show(); 
    } 

    public static void main(String[] args) { 

    JavaFXSpringApplication.args = args; 

    launch(args); 
    } 
} 

該類既JavaFX應用程序的入口點和彈簧引導初始化入口點,因此各地的可變參數的傳遞。通過導入外部配置文件,可以更輕鬆地保持主類不變,同時準備好其他Spring相關的東西(即設置Spring Data JPA,資源包,安全...)

在JavaFX「start」方法上,主ApplicationContext被初始化並生存。此時使用的任何bean都必須通過ApplicationContext.getBean()來獲取,但其他所有註釋的bean(只要它位於此主類的後代包中)都可以一直訪問。

特別是,控制器在這個其他類中聲明:

@Configuration 
@ComponentScan 
public class ApplicationConfiguration { 

    @Bean 
    public MainPaneController mainPaneController() throws IOException { 
    return (MainPaneController) this.loadController("path/to/MainPane.fxml"); 
    } 

    protected Object loadController(String url) throws IOException { 
    InputStream fxmlStream = null; 
    try { 
     fxmlStream = getClass().getResourceAsStream(url); 
     FXMLLoader loader = new FXMLLoader(); 
     loader.load(fxmlStream); 
     return loader.getController(); 
    } finally { 
     if (fxmlStream != null) { 
     fxmlStream.close(); 
     } 
    } 
    } 
} 

你可以看到任何控制器(我只有一個,但它可以是很多)標註有@Bean和全班是組態。

最後,這裏是MainPaneController。

public class MainPaneController { 

    @Autowired 
    private Service aService; 

    @PostConstruct 
    public void init() { 
    // ...stuff to do with components... 
    } 

    /* 
    * FXML Fields 
    */ 
    @FXML 
    private Node root; 

    @FXML 
    private TextArea aTextArea; 

    @FXML 
    private TextField aTextField; 

    @FXML 
    private void sayButtonAction(ActionEvent event) { 
    aService.doStuff(aTextArea, aTextField); 
    } 
} 

該控制器被聲明爲@Bean,因此它可以具有和從任何其他@Beans(或服務,組件等)來@Autowired。現在,例如,您可以讓它回答按鈕按鈕並將其字段上執行的邏輯委託給@Service。聲明爲Spring創建的控制器的任何組件都將由Spring進行管理,從而瞭解上下文。

配置起來非常簡單直接。隨意問你是否有任何疑問。

相關問題