Posted at
JavaFXDay 21

JavaFX × Spring Boot

More than 3 years have passed since last update.

この記事は JavaFX Advent Calendar 2015 - Qiita の 21 日目の記事です。

昨日は @fukai_yas さんの ScalaFXでのListViewの扱い でした。

明日は @orekyuu さんです。

JavaFX を Spring Boot から起動してみます。

といっても、素の Spring と連携させる方法はすでに多く情報が公開されています。

JavaFXのControllerにおけるSpringBeanのインジェクションについて - タツノオトシゴの日記

なので、 Spring Boot から起動するのもほぼ同じ方法でいけます。


build.gradle

buildscript {

repositories {
mavenCentral()
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.3.1.RELEASE'
}
}

apply plugin: 'java'
apply plugin: 'spring-boot'

sourceCompatibility = '1.8'
targetCompatibility = '1.8'
compileJava.options.encoding = 'UTF-8'

repositories {
mavenCentral()
}

dependencies {
compile 'org.springframework.boot:spring-boot-starter'
}


まずは普通に Spring Boot 用の build.gradle を用意して。。。


Main.java

package gl8080.javafx;

import java.io.IOException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

@SpringBootApplication
public class Main extends Application { // ★Application を継承

private static ConfigurableApplicationContext context;

public static void main(String[] args) throws IOException {
// ★ApplicationContext は後で使うので static 変数に保存しておく
context = SpringApplication.run(Main.class, args); // ★Spring Boot を起動
launch(args); // ★JavaFX を起動
}

@Override
public void start(Stage primaryStage) throws Exception {
// ★自作の FXMLLoader を Spring のコンテナから取得
MySpringFXMLLoader loader = context.getBean(MySpringFXMLLoader.class);

// ★fxml をロード
Parent root = loader.load("sample.fxml");

// ★あとは普通の JavaFX と同じ
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}

@Override
public void stop() throws Exception {
context.close(); // ★アプリ終了時に stop() メソッドがコールバックされるので、 Spring コンテナを終了させる
}
}


起動用のクラスを作ります。

クラス自体は JavaFX の Application を継承して、 JavaFX と同じ感じで作ります。

そして、 main() メソッドの先頭で Spring Boot を起動してから JavaFX を起動(launch())させます。

start() メソッドが呼びだされたら、 main() メソッドで保存しておいた ConfigurableApplicationContext から自作の FXMLLoader を取得します。

自作の FXMLLoader から fxml をロードしたら、あとは普通の JavaFX と同じです。

Spring Boot の終了処理は、 Application クラスの stop() メソッドがアプリケーション終了時にコールバックされることを利用して、そこで実行するようにしています。

自作の FXMLLoader は、以下のような感じです。


MySpringFXMLLoader.java

package gl8080.javafx;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;

@Component // ★コンポーネントとして登録
public class MySpringFXMLLoader {

@Autowired
private ApplicationContext context;

public Parent load(String path) throws IOException {
FXMLLoader loader = new FXMLLoader(); // ★オリジナルの FXMLLoader を生成

loader.setControllerFactory(this.context::getBean); // ★ControllerFactory に ApplicationContext を利用する

return loader.load(MySpringFXMLLoader.class.getClassLoader().getResourceAsStream(path));
}
}


処理自体は、ほとんどオリジナルの FXMLLoader に丸投げです。

唯一違うのが、 Controller のインスタンスを生成するための ControllerFactory を設定しているところです。

FXMLLoadersetControllerFacrorey() には javafx.util.Callback を実装したオブジェクトを渡します。

このインターフェースは関数型インターフェースになっていて、 ControllerClass<?> を受け取り、 Controller のインスタンスを返す必要があります。

それは、まさしく ApplicationContext#getBean() と一致するので、メソッド参照を使ってセットしています。

こうすることで、 Controller のインスタンスが Spring のコンテナから取得されるようになります。


MyController.java

package gl8080.javafx;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javafx.fxml.FXML;
import javafx.scene.control.Label;

@Component
public class MyController {

@FXML
private Label label;

@Autowired
private MyBean bean;

@FXML
public void onClickButton() {
String text = this.bean.getText();
this.label.setText(text);
}
}


コントローラクラスです。

Spring の @Autowired で他のビーンをインジェクションしています。

ボタンを押したら、ラベルの文字列をビーンが返した値で書き換えるだけの単純な実装です。


MyBean.java

package gl8080.javafx;

import org.springframework.stereotype.Component;

@Component
public class MyBean {

public String getText() {
return "Hello Spring Boot!!";
}
}


MyBean の実装はこんな感じ。

実際に動かしてみます。

問題なく連携できました!

コードは GitHub にあげています ( https://github.com/opengl-8080/javafx-and-spring-boot ) 。


おまけ

これで終わるのはアレなので、 Java EE のアドベントカレンダー で作ったライフゲームを JavaFX に移植してみました。

といっても、編集機能とかまで移植している余裕は、時間的にも体力的にもなかったので、とりあえずサンプルのゲーム定義だけを動かせるようにしました。

コードはこちら( https://github.com/opengl-8080/lifegame-javafx )です。

では、今回も彼に活躍していただきましょう!

(;゚д゚)デューーーーーーーーーーーーーーーーーーーーーーク!!!!


参考