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.

Table 1. Supernaut.FX Gradle vs Maven Comparison
Build System Features Plugins

Gradle

  • build jar

  • run

  • build native-looking app with bundled JRE with jpackage

  • build GraalVM natively-compiled image with GluonFX

  • micronaut-inject-java annotation processor

  • Java Modularity Plugin

  • JavaFX Gradle Plugin

  • Badass JLink Plugin

  • GluonFX Gradle Plugin

Maven

  • build jar

  • run

  • build GraalVM natively-compiled image with GluonFX

  • micronaut-maven-plugin

  • javafx-maven-plugin

  • gluonfx-maven-plugin

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

app.supernaut

Interfaces for background components (no JavaFX)

No dependencies!

app.supernaut.fx

JavaFX application dependency injection support

  • app.supernaut

  • JavaFX

  • slf4j-api

app.supernaut.fx.micronaut

App Launcher with dependency injection provided by Micronaut framework

  • app.supernaut.fx

  • micronaut-inject

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.

diag 1e9a6f45b7dac7ce54c847862e0294a9

4.1.2. Foreground & Background

diag e42c37733fa455fe71c9523fdfb57828

4.1.3. Foreground, Background, and Simplified Internals

diag 0e0d1b4fa1ef4242e398657dc632659a

5. Bibliography