Luke
Luke

Reputation: 283

Inject dependencies to the Java fx application

Probably I miss somehting out, but I'm struggeling to find a solution how I can pass dependencies like an instance of my event bus and some service interfaces to my javafx application.

I got an UI-Init class which does not much more than starting the application and receiving some dependencies for the UI like an eventBus:

public class Frontend {

public Frontend(MBassador<EventBase> eventBus) {
    Application.launch(AppGui.class);
}

My AppGui class extends Application and loads an FXML:

public class AppGui extends Application {

private Stage primaryStage;
private GridPane rootLayout;

@Override
public void start(Stage primaryStage) {
    this.primaryStage = primaryStage;
    try {
        // Load root layout from fxml file.
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("RootLayout.fxml"));
        rootLayout = (GridPane) loader.load();

        // Show the scene containing the root layout.
        Scene scene = new Scene(rootLayout);
        scene.setFill(null);
        primaryStage.setScene(scene);

        RootLayoutController rootController = loader.getController();
        rootController.init(/*here I would like to inject my eventBus*/);

        primaryStage.show();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Now, how can I pass my eventBus and other service interfaces to this controller? I've read about using DI frameworks like guice (How can JavaFX controllers access other services?) or afterburner.fx to use it. But even if I use guice for the rest of my application, somehow I need to pass the Injector instance to the JavaFX application?.

But Application.launch(AppGui.class); is static and internally creates an AppGui instance on the javafx thread, which I don't get access to. So how I can inject dependencies to my AppGui object without using static variables?

Upvotes: 3

Views: 1992

Answers (2)

Nikos Paraskevopoulos
Nikos Paraskevopoulos

Reputation: 40298

Here is what I do:

The Application class has a couple of lifecycle callbacks, init() and stop().

From the Javadoc:

public void init() throws java.lang.Exception

The application initialization method. This method is called immediately after the Application class is loaded and constructed. An application may override this method to perform initialization prior to the actual starting of the application.

public void stop() throws java.lang.Exception

This method is called when the application should stop, and provides a convenient place to prepare for application exit and destroy resources.

Also from the Javadocs, the lifecycle:

  1. Constructs an instance of the specified Application class
  2. Calls the init() method
  3. Calls the start(javafx.stage.Stage) method
  4. Waits for the application to finish, which happens when either of the following occur:
    • the application calls Platform.exit()
    • the last window has been closed and the implicitExit attribute on Platform is true
  5. Calls the stop() method

I start the Inversion of Control (IoC) container in init() and stop it in stop(). Now my Application class has a reference to the IoC container and can supply the first controller with its dependencies.

As a matter of fact, I let the IoC framework manage the controllers. I set them to the loaded FXML using FXMLLoader.setController(), instead of specifying them with fx:controller.

Upvotes: 4

Cypher
Cypher

Reputation: 2687

You can pass a static reference to your application class before you call launch(). Something like:

public class Frontend {
    public Frontend(MBassador<EventBase> eventBus) {
        AppGui.setEventBus(eventBus);
        Application.launch(AppGui.class);
    }
}

public class AppGui extends Application {
    private static MBassador<EventBase> eventBus;
    public static void setEventBus(MBassador<EventBase> eventBus) {
        this.eventBus = eventBus;
    }

    private MBassador<EventBase> eventBus;

    @Override
    public void init() {
        if (AppGui.eventBus == null) {
            throw new IllegalStateException(); 
            // or however you want to handle that state
        } else {
            this.eventBus = AppGui.eventBus;
            AppGui.eventBus = null; 
        }
    }
}

Whether you keep and use the static reference, or you copy the static reference to a local reference is up to you and the design of your application. If you expect to instantiate more than one copy of AppGui, you may need the local reference.

No idea if this is thread safe (probably not). The advice from @Nikos and @James_D is solid and preferred... but sometimes you just need a hack. :) YMMV

Upvotes: 1

Related Questions