友達をJavaFXとSpring Bootにする方法



少し前まで、このようなすばらしいフレームワークがSpring Bootとして登場しました。 点灯していない照明に照らして、Spring Bootとそのすべての「グッズ」とJavaFX 2の統合を検討したいと思います。



猫の下に興味のある人全員を招待します。



前文



Spring Bootは優れたフレームワークであり、一度しか試すことなく実行することはできません( 誰もが行うことお勧めします! )。 彼にとって完全に些細なことではないトピック、つまりJavaFXとの統合に触れたいと思います。 さて、退屈しないように、 ブラックジャックで簡単なリファレンスを作成し、データベースに接続します。



さあ始めましょう



Mavenプロジェクトの構成は、通常のSpring Bootアプリケーションと変わりません。



pom.xml
<dependencies> <!-- Spring Boot starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- Spring Boot JPA -    ,  --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <!-- H2  --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <!--  --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
      
      





アプリケーション設定ファイルも特別なものではありません。



application.properties
 #  UI ui.title = Spring Boot - JavaFX # JMX   ,       spring.jmx.enabled=false #      JPA spring.datasource.test-on-borrow=true spring.datasource.validation-query=SELECT 1 spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=create
      
      





しかし、アプリケーションへのエントリポイントを使用すると、すべてがはるかに興味深いものになります。

Springコンテキストを初期化する必要があり、これは2つの異なる場所で実行できます。



次の内容の抽象クラスを作成します。



AbstractJavaFxApplicationSupport.java
 package ru.habrahabr; import javafx.application.Application; import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; public abstract class AbstractJavaFxApplicationSupport extends Application { private static String[] savedArgs; protected ConfigurableApplicationContext context; @Override public void init() throws Exception { context = SpringApplication.run(getClass(), savedArgs); context.getAutowireCapableBeanFactory().autowireBean(this); } @Override public void stop() throws Exception { super.stop(); context.close(); } protected static void launchApp(Class<? extends AbstractJavaFxApplicationSupport> clazz, String[] args) { AbstractJavaFxApplicationSupport.savedArgs = args; Application.launch(clazz, args); } }
      
      





オーバーライドされたinit()メソッドに注意を払いたいです。

Springコンテキストの初期化を開始するのは、JavaFXの初期化時です。



 context = SpringApplication.run(getClass(), savedArgs);
      
      





さて、次の行で、現在のオブジェクトをビンで埋めます:



 context.getAutowireCapableBeanFactory().autowireBean(this);
      
      





上記の抽象クラスを継承して、JavaFXアプリケーションの動作を示します。 この段階では、DIと他のすべての春の「バン」をすでに使用できます。



Application.java
 @Lazy @SpringBootApplication public class Application extends AbstractJavaFxApplicationSupport { @Value("${ui.title:JavaFX }")// private String windowTitle; @Autowired private ControllersConfig.View view; @Override public void start(Stage stage) throws Exception { stage.setTitle(windowTitle); stage.setScene(new Scene(view.getParent())); stage.setResizable(true); stage.centerOnScreen(); stage.show(); } public static void main(String[] args) { launchApp(Application.class, args); } }
      
      





さて、今最も興味深いものに。

JavaFXは、コード(コントローラー)とビュー(ビュー)を分離する機能を提供します。ビューは、拡張子が* .fxmlのファイルにXML形式で保存されます。 ビュー自体には、素晴らしいUIエディターであるScene Builderがあります。

私はこのビューファイルのようなものを得ました:



main.fxml
 <?xml version="1.0" encoding="UTF-8"?> <?import javafx.geometry.Insets?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="284.0" prefWidth="405.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ru.habrahabr.ui.MainController"> <children> <TableView fx:id="table" editable="true" prefHeight="200.0" prefWidth="405.0" tableMenuButtonVisible="true" AnchorPane.bottomAnchor="50.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <columnResizePolicy><TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /></columnResizePolicy> </TableView> <HBox alignment="CENTER" layoutX="21.0" layoutY="207.0" prefHeight="50.0" prefWidth="300.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0"> <children> <TextField fx:id="txtName" promptText=""> <HBox.margin> <Insets right="3.0" /> </HBox.margin> </TextField> <TextField fx:id="txtPhone" promptText=""> <HBox.margin> <Insets right="3.0" /> </HBox.margin> </TextField> <TextField fx:id="txtEmail" promptText="E-mail"> <HBox.margin> <Insets right="3.0" /> </HBox.margin> </TextField> <Button minWidth="-Infinity" mnemonicParsing="false" onAction="#addContact" text="" /> </children> </HBox> </children> </AnchorPane>
      
      





このファイルのリストは読みにくいですが、 fx属性がルート要素に指定されていることに注意してください:controller = "en.habrahabr.ui.MainController" 。 このビューコンポーネントに使用するコントローラークラスを示します。 また、ネストされた要素の場合、 fx:id =“ txtEmail”属性は、コントローラーのどのフィールドに挿入するかを示します。 問題は、JavaFX(@FXMLアノテーションで定義されている)とスプリングインジェクションからフレンドコントローラーのインジェクションを作成することです。 なぜなら、標準のFXMLローダーを使用する場合、スプリングは新しいコントローラーオブジェクトを認識せず、それに応じて注入を行わないためです。

コントローラー自体を書きましょう:



MainController.java
 package ru.habrahabr.ui; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import ru.habrahabr.entity.Contact; import ru.habrahabr.service.ContactService; import javax.annotation.PostConstruct; import java.util.List; public class MainController { //  Spring @Autowired private ContactService contactService; //  JavaFX @FXML private TableView<Contact> table; @FXML private TextField txtName; @FXML private TextField txtPhone; @FXML private TextField txtEmail; //  private ObservableList<Contact> data; /** *    JavaFX. *      FXML    . * *  ,    <b></b>   "initialize", *   ,   . * *         *      , *   @PostConstruct. *   ,  , *       . * {@link MainController#init()} */ @FXML public void initialize() { } /** *        . */ @PostConstruct public void init() { List<Contact> contacts = contactService.findAll(); data = FXCollections.observableArrayList(contacts); //     TableColumn<Contact, String> idColumn = new TableColumn<>("ID"); idColumn.setCellValueFactory(new PropertyValueFactory<>("id")); TableColumn<Contact, String> nameColumn = new TableColumn<>(""); nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); TableColumn<Contact, String> phoneColumn = new TableColumn<>(""); phoneColumn.setCellValueFactory(new PropertyValueFactory<>("phone")); TableColumn<Contact, String> emailColumn = new TableColumn<>("E-mail"); emailColumn.setCellValueFactory(new PropertyValueFactory<>("email")); table.getColumns().setAll(idColumn, nameColumn, phoneColumn, emailColumn); //     table.setItems(data); } /** * ,      "". *     FXML  . */ @FXML public void addContact() { Contact contact = new Contact(txtName.getText(), txtPhone.getText(), txtEmail.getText()); contactService.save(contact); data.add(contact); //   txtName.setText(""); txtPhone.setText(""); txtEmail.setText(""); } }
      
      





Springになじみのないオブジェクトにそれを注入させる方法を理解することは残っています。 そしてその秘密は、もう1つのSpring Boot構成クラスにあります。



ConfigurationControllers.java
 @Configuration public class ConfigurationControllers { @Bean(name = "mainView") public View getMainView() throws IOException { return loadView("fxml/main.fxml"); } /** *          , *       . */ @Bean public MainController getMainController() throws IOException { return (MainController) getMainView().getController(); } /** *     FXML . *  -      -, *   FXML      . */ protected View loadView(String url) throws IOException { InputStream fxmlStream = null; try { fxmlStream = getClass().getClassLoader().getResourceAsStream(url); FXMLLoader loader = new FXMLLoader(); loader.load(fxmlStream); return new View(loader.getRoot(), loader.getController()); } finally { if (fxmlStream != null) { fxmlStream.close(); } } } /** *  - :       , *  view - ,       {@link Application}. */ public class View { private Parent view; private Object controller; public View(Parent view, Object controller) { this.view = view; this.controller = controller; } public Parent getView() { return view; } public void setView(Parent view) { this.view = view; } public Object getController() { return controller; } public void setController(Object controller) { this.controller = controller; } } }
      
      





以上で、Spring Bootに統合されたJavaFXアプリケーションが手に入り、そのすべての巨大な機能が開かれました。







ソースへのリンク: github.com/ruslanys/sample-springboot-javafx



PS誰かがこの投稿を手伝ってくれたら嬉しいです。 ヒントと修正に感謝します。



JavaFXとSpring Bootの統合のすべての複雑さを説明するこの優れた記事について、Spring BootとStephen Chinを試してくれたxolvoに感謝します。



UPD。 編集してくれたISergiusに感謝します。



All Articles