Préambule
Dans mon travail de tous les jours je développe des applications web avec des technologies comme JSF + Spring + Hibernate, Angular + Spring Boot + Spring Data / Hibernate, etc...
Lorsque je me suis intéressé à JavaFX pour un petit projet personnel, j'ai commencé à développer mon application en utilisant le plugin e(fx)clipse afin de générer le projet.
Ensuite j'ai voulu ajouter une base de données fichiers pour sauvegarder les données de l'application, ajouter des librairie pour générer du PDF, etc... Et la je me suis rendu compte que toutes les choses bien pratiques que j'utilisais tous les jours dans le cadre du développement d'applications web (tel que l'IoC, l'ORM, la gestion des dépendances, ...) je devais les gérer ou les ajouter manuellement.
Partant de ce constat je me suis dit que j'allais faire de mon projet JavaFX un projet Maven et ensuite je me suis demandé s'il ne serait pas possible de marier JavaFX et Spring Boot pour profiter des avantages de ce dernier.
Et nous voilà donc dans cet article pour voir comment tout cela s'articule. Bon allé j'arrête de raconter ma vie, fort intéressante vous en conviendrez, et passons au coté technique.
Technos
Tout d'abord voici la liste des technologies utilisées dans cet article et leurs version au moment de l'écriture.
- JavaFX 8 (version de la JRE 8 en l’occurrence)
- Maven 3.x (on pourrait aussi bien utiliser Gradle)
- Spring Boot 2.0.5
Tutoriel
Tout d'abord je tiens à préciser que cet article n'a pas pour but d'apprendre le développement d'une application JavaFX, il y a déjà pléthore d'articles sur le net pour cela, je vous laisse rechercher ça dans votre moteur de recherche favoris.
Spring Boot
Pour commencer nous allons générer une application Spring Boot, pour cela personnellement j'utilise Spring Initializr qui permet de choisir ce que vous voulez ajouter dans votre application.
Choisissez Maven project, remplissez Group et Artifact, puis générez le projet.
Une fois le fichier zip téléchargé décompressez le et copiez le dans votre workspace afin de l'ouvrir avec votre IDE favoris (Pour ma part Eclipse).
Vous devriez vous retrouver avec quelque chose comme ça :
Choisissez Maven project, remplissez Group et Artifact, puis générez le projet.
Une fois le fichier zip téléchargé décompressez le et copiez le dans votre workspace afin de l'ouvrir avec votre IDE favoris (Pour ma part Eclipse).
Vous devriez vous retrouver avec quelque chose comme ça :
Pour le moment le fichier pom.xml ressemble à ce qui suit :
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>fr.jbe</groupId> <artifactId>jfxspringboot</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>jfxspringboot</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Et le main de l'application devrait ressembler à quelque chose comme ça :
package fr.jbe.jfxspringboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class JfxspringbootApplication { public static void main(String[] args) { SpringApplication.run(JfxspringbootApplication.class, args); } }
Nous avons maintenant notre main classe avec l'annotation @SpringBootApplication qui permet de pré-configurer ce qui est nécessaire pour utiliser Spring. Bien sûr comme pour JavaFX je ne vais pas entrer dans le détail du fonctionnement de Spring Boot car ce n'est pas le sujet de cet article. Vous pouvez vous renseigne directement sur le site de Spring https://spring.io/projects/spring-boot.
JavaFX
Nous pouvons maintenant intégrer JavaFX à notre application Spring Boot. Pour cela il faut commencer par faire étendre Application.java à notre main classe.
Puis implémenter la méthode start() de l'application JavaFX dans laquelle on va construire la fenêtre principal de l'application puis l'afficher.
package fr.jbe.jfxspringboot; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import javafx.application.Application; import javafx.stage.Stage; @SpringBootApplication public class JfxspringbootApplication extends Application { public static void main(String[] args) { SpringApplication.run(JfxspringbootApplication.class, args); } @Override public void start(Stage primaryStage) throws Exception { // TODO Auto-generated method stub } }
Ensuite afin de mieux intégrer Spring Boot à l'applicarion JavaFX nous allons récupérer le contexte Spring qui nous permettra plus tard d'injecter les contrôleurs lors du chargement des vue FXML.
Il faut donc ajouter la variable de contexte à notre classe, puis implémenter la méthode init() de JavaFX pour démarrer Spring Boot via la méthode run() et récupérer le contexte. La main méthode appelle maintenant la méthode launch() de JavaFX pour lancer l'application.
private ConfigurableApplicationContext context; public static void main(String[] args) { launch(JfxspringbootApplication.class ,args); } @Override public void init() throws Exception { context = SpringApplication.run(JfxspringbootApplication.class); }
Voilà nous avons notre contexte prêt à être utilisé. Je prends le parti ici d'utiliser la création d'interfaces graphiques via les fichiers FXML pour profiter de l'utilisation de l'éditeur SceneBuilder qui est bien pratique. Mais bien sûr ce n'est pas obligatoire et on peut très bien construire les IHMs programmatiquement.
FMXL Loader
Afin de tirer partie de l'utilisation de Spring et de son contexte nous allons créer un loader pour notre application qui va intégrer le contexte au loader FMXL de JavaFX.
package fr.jbe.jfxspringboot.view; import java.net.URL; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Component; import javafx.fxml.FXMLLoader; @Component public class AppFXMLLoader { @Autowired private ConfigurableApplicationContext context; public FXMLLoader getLoader(URL url) { FXMLLoader loader = new FXMLLoader(url); loader.setControllerFactory(context::getBean); return loader; } public FXMLLoader getLoader(String fxmlPath) { return getLoader(this.getClass().getResource(fxmlPath)); } }
Comme on peut le voir ce loader créé une instance FXMLLoader en lui donnant le context::getBean comme controllerFactory ce qui lui permet d'utiliser le contexte Spring pour demander une instance du contrôleur associé à la vue FXML. Nous pouvons constater au passage que nous utilisons l'annotation @Autowired pour injecter le contexte.
Nous disposons maintenant d'un composant que nous allons pouvoir utiliser pour construire nos vue et notre scène.
Fichier FXML
Tout d'abord il nous faut créer le FXML qui va correspondre à la fenêtre principale de notre application. faisons quelque chose de simple.
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.geometry.Insets?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TextField?> <?import javafx.scene.layout.BorderPane?> <?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.RowConstraints?> <BorderPane maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="200.0" prefWidth="400.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.141" fx:controller="fr.jbe.jfxspringboot.view.controller.MainController"> <center> <GridPane hgap="10.0" BorderPane.alignment="CENTER"> <columnConstraints> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> </columnConstraints> <rowConstraints> <RowConstraints maxHeight="-Infinity" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="-Infinity" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="-Infinity" minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> </rowConstraints> <children> <TextField fx:id="name" /> <Button mnemonicParsing="false" onAction="#handleHello" text="Hello" GridPane.columnIndex="1" /> <Label text="Bonjour" GridPane.halignment="RIGHT" GridPane.rowIndex="1" /> <Label fx:id="displayName" text="Label" GridPane.columnIndex="1" GridPane.rowIndex="1" /> </children> <BorderPane.margin> <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> </BorderPane.margin> </GridPane> </center> </BorderPane>
Voilà juste un textfield, un bouton et un label pour afficher le résultat.
FXML controller
Il faut maintenant associé un contrôleur à notre vue FXML afin de réagir à l'appui sur le bouton. C'est ce contrôleur qui sera gérer via le contexte Spring.
package fr.jbe.jfxspringboot.view.controller; import org.springframework.stereotype.Component; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.TextField; @Controller public class MainController { @FXML private TextField name; @FXML private Label displayName; @FXML private void initialize() { name.clear(); displayName.setText(""); } @FXML public void handleHello() { if (!name.getText().isEmpty()) { displayName.setText(name.getText()); } } }
Notez bien de la classe du contrôleur est annotée @Controller afin d'être prise en compte par l'IoC de Spring.
Créer la vue
Je vais maintenant créer une classe pour la création de la vue principale de l'application. Cette classe a pour but de charger la vue FXML principale et d'effectuer les opérations nécessaires, ici pas grand chose mais on pourrait imaginer setter des données sur le contrôleur. Elle permet aussi d'initialiser l'utilisation de l'IoC car comme vous le savez si vous instanciez à la main une classe elle ne peut plus bénéficier du DI.
package fr.jbe.jfxspringboot.view; import java.io.IOException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.scene.layout.BorderPane; @Component public class MainView { @Autowired AppFXMLLoader appFXMLLoader; public Scene createScene() { Scene scene = null; try { FXMLLoader loader = appFXMLLoader.getLoader(MainView.class.getResource("fxml/MainLayout.fxml")); BorderPane borderPane = loader.load(); scene = new Scene(borderPane); } catch (IOException e) { e.printStackTrace(); } return scene; } }
Il ne nous reste plus qu'à implémenter la méthode start() de notre application.
Démarrons l'application
Voici l'intégration de tous ce que nous avons parlé au-dessus. Dans la classe JfxspringbootApplication.java il faut implémenter la méthode start() comme suit.
@Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("Hello guys !"); MainView mainView = context.getBean(MainView.class); primaryStage.setScene(mainView.createScene()); primaryStage.show(); }
Maintenant si nous lançons l'application nous devrions voir quelque chose qui ressemble à ce qui suit.
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.5.RELEASE) 2018-09-21 17:05:09.419 INFO 13800 --- [JavaFX-Launcher] o.s.boot.SpringApplication : Starting application on NUANDA-PC with PID 13800 (started by nuanda in D:\Dev\workspace\workspace_oxygen\jfxspringboot) 2018-09-21 17:05:09.422 INFO 13800 --- [JavaFX-Launcher] o.s.boot.SpringApplication : No active profile set, falling back to default profiles: default 2018-09-21 17:05:09.459 INFO 13800 --- [JavaFX-Launcher] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@79e35f75: startup date [Fri Sep 21 17:05:09 CEST 2018]; root of context hierarchy 2018-09-21 17:05:09.885 INFO 13800 --- [JavaFX-Launcher] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2018-09-21 17:05:09.896 INFO 13800 --- [JavaFX-Launcher] o.s.boot.SpringApplication : Started application in 0.707 seconds (JVM running for 1.206)
Avec la fenêtre de l'application.
Conclusion
Voilà pour les grandes lignes de la création d'une application JavaFX, Spring Boot et Maven. Comme vous avez pu le voir il n'y a rien de très compliqué, on peut bien sûr ajouter bien d'autre choses tel qu'une datasource pour se connecter à une base de données par exemple, mais cela sera peut être le sujet d'un prochain article.
J'espère que cet article vous aura intéressé et n’hésitez pas à laisser un commentaire si vous avez des questions ou des remarques constructives.
Merci d'avoir pris le temps de lire cet article et à bientôt.
Retrouvez les sources sur github.com
Voir l'article suivant : JavaFX + Spring Boot + Database
Aucun commentaire :
Enregistrer un commentaire