Adding Spring Dependency Injection in JavaFX (JPA Repo, Service)

Dependency injection options for JavaFX

There are numerous ways to get dependency injection into a JavaFX application. For example Gluon have a project called Gluon Ignite which enables JavaFX application for various dependency injection frameworks, such as Guice, Spring and Dagger.

As you have chosen Spring for your dependency injection framework and you wish to use a bunch of other Spring facilities such as Spring Data repositories, you may wish to consider using a SpringBoot application.

You could make your JavaFX application a SpringBoot application (though this isn't strictly necessary just to get dependency injection) in order to get a bunch of Spring facilities available within your application. There are some tutorials on that on the web if you search around.

Basic sample integration of Spring and JavaFX

Here is an example of a tutorial on integrating JavaFX with a SpringBoot application:

  • Let Spring Be Your JavaFX Controller Factory

A critical part of that example is the init() method of the application (which I have just copy and pasted and reproduced here for reference):

@SpringBootApplication
public class DemoApplication extends Application {

    private ConfigurableApplicationContext springContext;
    private Parent root;

    @Override
    public void init() throws Exception {
        springContext = SpringApplication.run(DemoApplication.class);
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/sample.fxml"));
        fxmlLoader.setControllerFactory(springContext::getBean);
        root = fxmlLoader.load();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("Hello World");
        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    @Override
    public void stop() throws Exception {
        springContext.stop();
    }


    public static void main(String[] args) {
        launch(DemoApplication.class, args);
    }
}

The sample app is running the SpringBoot application to startup the Spring system and have it provide an application context in the init method. The app then uses the FXMLLoader setControllerFactory() method to allow Spring to instantiate FXML controllers and inject references to Spring beans in the application.

Auto-wiring your JavaFX Controllers

To get your JAVAFX FXML controller autowired, in addition to the following call one the FXMLLoader:

fxmlLoader.setControllerFactory(springContext::getBean);

You also need to annotate your class as a Spring @Component, and @Autowired in any Spring dependencies you want your controller to use. In this way, the FXMLLoader will inject the @FXML based references to your UI elements and it will also delegate to the spring context to inject the Spring dependencies.

@Component
public class DemoController {
    @FXML
    private Label usernameLabel; 

    @Autowired
    public void mySpringService;

    public void initialize() {
        usernameLabel.setText(
            mySpringService.getLoggedInUsername()
        );
    }
}

Note, Spring has an @Controller annotation, which could be used to annotate the JavaFX controller rather than the @Component annotation, but I would recommend avoiding use of @Controller for that purpose, and instead @Controller annotation for Spring REST service endpoint controller definitions.

Separation of concerns between the Spring Boot Application and JavaFX Application

One thing you might want to be careful of is that running the SpringBoot application, generates a new instance of the application and you already have a JavaFX application instance launched by the JavaFX system, so that would result in two JavaFX application instances if the SpringBoot application and the JavaFX application are based upon the same class (as shown above), which would potentially be confusing.

So it may is likely better to separate out the Spring application and the JavaFX application. This enhances the separation of concerns between the UI and service portions of the application and makes for easier testing as the Spring application can be unit tested independently of of starting up and shutting down the JavaFX application.

Auto-wiring the JavaFX application class

Note, using the above setup, it will not autowire the JavaFX application class instantiated instance. If you wish to do that, you can use the technique illustrated below to inject beans in the JavaFX instantiated application class:

  • Injecting beans into a class outside the Spring managed context

Place the following code inside your application's init method:

springContext
    .getAutowireCapableBeanFactory()
    .autowireBeanProperties(
        this,
        AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, 
        true
    );

The mvvmFX framework uses a similar method to that outlined above to integrate SpringBoot with JavaFX applications:

  • mvvmFX code for integrating SpringBoot.

Passing command line arguments from JavaFX to SpringBoot

To pass arguments from the JavaFX application to the SpringBoot application, use:

SpringApplication.run(
    DemoApplication.class, 
    getParameters().getRaw().toArray(new String[0])
);

Other issues

If you need, even more control over the startup of the SpringApplication, you can use the SpringApplicationBuilder for example:

ConfigurableApplicationContext startupContext =
        new SpringApplicationBuilder(DemoApplication.class)
                .web(WebApplicationType.NONE)
                .run(args);

This answer is just written to give you hints on how you might approach this problem rather than as a general purpose guide on how to integrate dependency injection with JavaFX, which could end being quite a tricky subject to cover comprehensively.