Application.launch()
запустит среду выполнения JavaFX, поток приложения JavaFX и, как указано в вопросе, создаст экземпляр подкласса Application
. Затем он вызывает методы жизненного цикла для этого экземпляра Application
.
В большинстве примеров используется только метод Application.start()
, который выполняется в потоке приложения JavaFX, но есть и другие методы жизненного цикла (которые по умолчанию не- ops), которые также выполняются. Метод init()
выполняется до start()
. Он выполняется в основном потоке, но гарантированно завершится до вызова start()
. Метод stop()
вызывается при выходе из приложения FX. Оба эти метода вызываются в том же экземпляре Application
, что и метод start()
.
Одним из решений является использование этих методов жизненного цикла для запуска, настройки и очистки инфраструктуры внедрения зависимостей. Итак, здесь мы по существу используем жизненный цикл JavaFX и подключаемся к нему для ручного управления жизненным циклом инфраструктуры DI. Вот пример с Spring Boot:
package org.jamesd.examples.springfx;
import java.io.IOException;
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
@SpringBootApplication
public class App extends Application {
private ConfigurableApplicationContext context ;
@Override
public void init() {
List<String> rawParams = getParameters().getRaw() ;
String[] args = rawParams.toArray(new String[rawParams.size()]) ;
context = SpringApplication.run(getClass(), args);
// further configuration on context as needed
}
@Override
public void start(Stage stage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
loader.setControllerFactory(context::getBean);
Parent root = loader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
@Override
public void stop() {
context.close();
}
public static void main(String[] args) {
launch();
}
}
Альтернативный подход - обойти стандартный процесс запуска JavaFX. JavaFX 9 и более поздние версии имеют метод Platform.startup()
, который «вручную» запускает среду выполнения JavaFX, запускает поток приложения FX и вызывает предоставленный Runnable
в этом потоке. Обходя обычный процесс запуска JavaFX, вы можете использовать процесс запуска вашей инфраструктуры DI и вызывать Platform.startup()
по мере необходимости в соответствующем месте этого процесса. В некотором смысле это подход, противоположный предыдущему: мы используем жизненный цикл DI Framework и подключаемся к нему для ручного управления жизненным циклом приложения FX.
Вот пример такого подхода, снова с использованием Spring Boot:
package org.jamesd.examples.springfx;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
@SpringBootApplication
public class App implements CommandLineRunner {
@Autowired
private ConfigurableApplicationContext context ;
private void startUI() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
loader.setControllerFactory(context::getBean);
Parent root = loader.load();
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
SpringApplication.run(App.class, args) ;
}
@Override
public void run(String... args) throws Exception {
// perform additional configuration on context, as needed
Platform.startup(this::startUI);
}
}
Обратите внимание, что в этом подходе Spring Boot управляет своим собственным жизненным циклом, поэтому он будет «знать», что нужно вызывать любые @PreDestroy
-аннотированные методы при завершении работы (приведенный ниже пример кода содержит такой метод в the MessageBean
).
Для полноты, вот оставшиеся классы, файлы F XML и конфигурация, чтобы сделать этот пример полным. Любая из указанных выше версий App
будет работать с этими файлами:
org.jamesd.examples.springfx.Config:
package org.jamesd.examples.springfx;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
@Bean
public MessageBean messageBean() {
return new MessageBean();
}
@Bean
public Controller controller() {
return new Controller();
}
}
org.jamesd.examples.springfx.Controller:
package org.jamesd.examples.springfx;
import org.springframework.beans.factory.annotation.Autowired;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class Controller {
@Autowired
private MessageBean messageBean ;
@FXML
private Label welcomeLabel ;
@FXML
private void initialize() {
welcomeLabel.setText(messageBean.getMessage());
}
}
org.examples.jamesd.springfx.MessageBean:
package org.jamesd.examples.springfx;
import javax.annotation.PreDestroy;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "springfx")
public class MessageBean {
private String message ;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@PreDestroy
public void dispose() {
System.out.println("Closing");
}
}
org.jamesd.examples.springfx.Main.f xml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<VBox alignment="CENTER" minWidth="200" minHeight="200" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.jamesd.examples.springfx.Controller">
<Label fx:id="welcomeLabel" />
</VBox>
application.properties:
springfx.message=Spring Boot JavaFX Application
информация о модуле. java:
module org.jamesd.examples.springfx {
requires transitive javafx.controls;
requires javafx.fxml;
requires spring.boot;
requires spring.beans;
requires spring.context;
requires spring.core ;
requires spring.boot.autoconfigure;
requires java.annotation;
opens org.jamesd.examples.springfx to javafx.fxml, spring.context, spring.beans, spring.core ;
exports org.jamesd.examples.springfx;
}
пом. xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.jamesd.examples</groupId>
<artifactId>springfx</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>14</maven.compiler.source>
<maven.compiler.target>14</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>14</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>14</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.1</version>
<configuration>
<mainClass>org.jamesd.examples.springfx.App</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>