1. Introduction
Supernaut.FX is a lightweight dependency injection framework for JavaFX applications. It is a wrapper around the core dependency injection capability of Micronaut® framework (provided by the micronaut-inject
JAR.) It enables the use of dependency injection in application, controller, and service objects.
This early-draft User’s Guide provides an introduction to the concepts and major classes and interfaces in the Supernaut.FX framework.
2. Features and Benefits
TBD. (See the README for now.)
3. Getting Started
This getting started section assumes you are already familiar with the basics of writing a JavaFX application.
3.1. Sample/Starter Projects on GitHub
The fastest way to get started is to clone or download one of the two sample "Hello World" projects on GitHub. There is one project that uses the Maven build system and one that uses Gradle. Currently — due to the state of the available plugins and my limited knowledge of how to use them — the options for deploying your applications are different on Gradle vs. Maven.
3.1.1. Gradle Sample
Using supernaut-fx-sample-gradle, via the Badass JLink Plugin, you can build native-looking macOS, Windows, and Linux applications with a bundled Java Runtime.
The Badass JLink Plugin performs an important role in converting non-modular jars (especially the Micronaut jars) to work in a modular environment.
You can also build natively-compiled images via the GluonFX Gradle Plugin and the GraalVM native-image
capability.
3.1.2. Maven Sample
Using supernaut-fx-sample-maven, via the GluonFX Maven Plugin, you can build natively-compiled macOS, Windows, and Linux applications using the GraalVM native-image tool.
3.1.3. Comparison Matrix
Eventually, you should be able to build `jpackage’d images with Maven, as well.
Build System | Features | Plugins |
---|---|---|
Gradle |
|
|
Maven |
|
|
3.2. Converting a simple JavaFX Application to Supernaut.FX
The following listing shows a simple hello-world style JavaFX program.
public class MinimalApp extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
var label = new Label("Hello, Earth!");
var scene = new Scene(new StackPane(label), 300, 200);
primaryStage.setScene(scene);
primaryStage.setTitle("JavaFX Minimal App");
primaryStage.show();
}
}
By simply replacing extends Application
with implements ApplicationDelegate
and using the launch()
method of the FXLauncher
service found with the default find()
method, it becomes a Supernaut.FX application. Also note that the jakarta.inject
@Singleton
annotation has been added.
@Singleton
public class MinimalApp implements ApplicationDelegate {
public static void main(String[] args) {
FxLauncher.find().launch(args, MinimalApp.class);
}
@Override
public void start(Stage primaryStage) {
var label = new Label("Hello, Earth!");
var scene = new Scene(new StackPane(label), 300, 200);
primaryStage.setScene(scene);
primaryStage.setTitle("SupernautFX Minimal App");
primaryStage.show();
}
}
This allows you to begin using Dependency Inject to configure your application. You can define an AppConfig
class as follows:
@Singleton
public static class AppConfig {
/** the application name */
public final String planetName = "Mars";
}
and inject it into an added constructor of MinimalApp
:
@Singleton
public class MinimalApp implements ApplicationDelegate {
private final static String planetName;
public static void main(String[] args) {
FxLauncher.find().launch(args, MinimalApp.class);
}
public MinimalApp(AppConfig config) {
planetName = config.planetName;
}
@Override
public void start(Stage primaryStage) {
var label = new Label("Hello, " + planetName + "!");
var scene = new Scene(new StackPane(label), 300, 200);
primaryStage.setScene(scene);
primaryStage.setTitle("SupernautFX Minimal App");
primaryStage.show();
}
}
3.3. Dependency Injecting FXML Controllers
To load a dependency-inject FXML controller, simply add a FxmlLoaderFactory
to one of your injected controllers, like this:
public MinimalApp(AppConfig config, FxmlLoaderFactory injectedLoaderFactory) {
planetName = config.planetName;
loaderFactory = injectedLoaderFactory;
}
and then use it to load you FXML-based windows and contollers.
FXMLLoader loader = loaderFactory.get(getFXMLUrl("MainWindow.fxml"));
Parent root = loader.load();
See HelloApp
and MainWindowController
(links TBD) for a fully-functioning example.
3.4. Injecting Service Objects
See GreetingService
(link TBD) for a functioning example.
3.5. Built-in Injectable Services
TBD.
-
FxmlLoaderFactory
-
BrowserService
-
HostServices
-
Application
3.6. Using jakarta.inject Annotations
jakarta.inject
annotations, such as @Singleton
and @Named
are the preferred type of annotations to use for injection as they are equivalent to and replace the older javax.inject
annotation.
These annotations can work with multiple dependency injection frameworks and will help you write more portable code.
3.7. Using Micronaut Annotations
Unfortunately, most real-world applications will need to use some of the Micronaut-provided annotations, such as @Factory
.
3.8. Java Modules
Supernaut.FX currently consists of 3 Java modules (JARs)
Module name | Purpose | Dependencies |
---|---|---|
Interfaces for background components (no JavaFX) |
No dependencies! |
|
JavaFX application dependency injection support |
|
|
App Launcher with dependency injection provided by Micronaut framework |
|
Typical applications will have a compile-time dependency on app.supernaut.fx
and a runtime dependency on app.supernaut.fx.micronaut
.
You may also develop library modules for services depending only on app.supernaut
.
4. Multi-threaded Startup
- NOTE
-
This section is for advanced users only. The parallel, asynchronous launch features should be considered experimental.
Supernaut allows you to initialize background services in parallel with the initialization of JavaFX and your foreground application running on the JavaFX Application Thread. This allows you to make network requests as early as possible so that their results will be available before or shortly after display of the application main Window.
Creating a BackgroundApplication
is optional, there is a DefaultBackgroundApplication
that loads no services and can be used for simple applications or in early-stage development. When you are ready to add background services, you can create a BackgroundApplication
class.
- NOTE
-
JavaFX provides the
Preloader
class that can display a simple window while the main window is loading. For now, Supernaut is ignoring the preloader capability and instead focusing on displaying the main window as quickly as possible and then updating it with content in the background. The philosophy of a Supernaut application is to display the main window as quickly as a preloader and then update its contents as data becomes available. In those use cases, there is little benefit to displaying a preloader window. If there is a use case that needing preloader capability and/or a contributor with a pull request adding support, Supernaut can be updated to launch an optional preloader at the appropriate time.
4.1. Supernaut/FX Sequence Diagrams
4.1.1. Foreground Only
This diagram shows the simplest Supernaut configuration from the perspective of the developer of a ApplicationDelegate
(aka foreground application). It shows the four methods that an implementor may implement (all but start()
are optional) and shows the order in which they are called and what threads they are called on. This behavior is identical to a normal JavaFX app that subclasses Application
but with the added capability of having dependencies injected into the constructor.