All articles in this series
- JavaFX with Spring Boot Part-01: Introduction
- JavaFX with Spring Boot Part-02: Basic Integration
- JavaFX with Spring Boot Part-03: Advanced Integration
- JavaFX with Spring Boot Part-04: Using FXML
- JavaFX with Spring Boot Part-05: FxWeaver Integration
In the previous article, we’ve seen a basic/minimal integration of Spring boot with JavaFX. In this article we’re going to see some extra wiring up to get the most out of Spring Boot.
Creating the Application Context
In the previous article, we had extended JavaFX’s Application
abstract class by overriding its start
method. start
method and its close
counter part are called on the UI thread. On the other hand the init
method is called on a non-ui thread and the start
is only called after the init
method returns. This makes the init
method the perfect place to create our ApplicationContext
.
private ConfigurableApplicationContext context;
@Override
public void init() {
ApplicationContextInitializer<GenericApplicationContext> initializer =
context -> {
context.registerBean(Application.class, () -> JavaFxApp.this);
context.registerBean(Parameters.class, this::getParameters);
};
this.context = new SpringApplicationBuilder()
.sources(SpringBootApp.class)
.initializers(initializer)
.run(getParameters().getRaw().toArray(new String[0]));
}
Closing the Application Context
According to the official docs, stop
method “provides a convenient place to prepare for application exit and destroy resources”. It is the right place to close the application context as well as to terminate the UI thread by calling Platform.exit()
.
@Override
public void stop() {
this.context.close();
Platform.exit();
}
Decoupling UI Creation Code from the Application Configuration Code
In the previous article, we had overridden the start
method as follows. Since we only want application-wide initialization and termination related code in the JavaFxApp
class, this is not ideal.
@Override
public void start(Stage primaryStage) throws Exception {
Pane helloPane = new Pane(new Label("Hello JavaFx"));
primaryStage.setScene(new Scene(helloPane, 200, 100));
primaryStage.show();
}
Spring provides a great way of communication among components by firing events. We will be using this feature for various things in the upcoming articles. For now, let us make use of it to move the UI creation code in its own place. Since start
method is called when the stage
object is ready (in other words JavaFX application thread aka UI thread is ready), we want to publish an event in the start
method and handle the event in some where else.
Let’s first create an event class for our case.
public class StageReadyEvent extends ApplicationEvent {
@Getter
private final Stage stage;
public StageReadyEvent(Stage stage) {
super(stage);
this.stage = stage;
}
}
Then create a Listener
for this type of event. Since we’ll be creating various events and listeners in the future, it is good the create a new package called event
and put all events and their corresponding listeners there. Also, I highly recommend XYZEvent
and XYZEventListener
pattern for naming event and listener classes.
@Component
public class StageReadyEventListener implements ApplicationListener<StageReadyEvent> {
private final ApplicationContext applicationContext;
private final String applicationTitle;
public StageReadyEventListener(ApplicationContext applicationContext,
@Value("${app.title}") String applicationTitle) {
this.applicationTitle = applicationTitle;
this.applicationContext = applicationContext;
}
@Override
public void onApplicationEvent(StageReadyEvent event) {
Stage stage = event.getStage();
Pane helloPane = new Pane(new Label("Hello JavaFx"));
stage.setScene(new Scene(helloPane, 200, 100));
stage.setTitle(this.applicationTitle);
stage.show();
}
}
With @Component
annotation, spring will take care of creating a StageReadyEventListener
object for us. The ApplicationContext
we’ve created in the init
method will be injected into the constructor by Spring as well. Let’s make use of another Spring goodie by defining a title for our application in the application.properties
file.
app.title=Hello JavaFX with Spring Boot
As you can see, with the @Value
annotation above, Spring will inject the value automatically for us. This way we can keep all application configuration in the application.properties
and access them where ever we wan’t. Isn’t that cool? Note that we could do the same for the Hello JavaFx
text of the Label
. I’ll leave it as an exercise for the reader.
Finally, lets publish the event in the start
method.
@Override
public void start(Stage primaryStage) throws Exception {
this.context.publishEvent(new StageReadyEvent(primaryStage));
}
We’re ready to run our application. You should see something similar.
In the next article, we’ll use FXWeaver
.
Source code for this article can be found here