Issue
I have a java FX basic application which has a simple Scene(a form). I have a Mysql Db and I am using Spring JPA (spring data jpa i.e repository/entities) to interact with the same.
Now, since we know that javaFx has some lifecycle hooks namely: init() start() and stop().
Let's say I want to insert data in Database using JPA save() method. Usually, if it was my controller, a normal DB injection like:
@Autowired
EmployeeRepo employeeRepo;
Would have worked. But, I am not able to access this (or any Autowired Injection) inside the lifecycle methods.
public void start(Stage primaryStage) throws Exception {
// Some Code
employeeRepo.findAll() <- This is returning null
However, when I add a test method and use the same, It works fine:
@PostConstruct
public void test() {
// Repo object is not giving null
}
Is there a way I can manually inject the dependencies inside my button listener or pass it to the launch method.
Please let me know if there is a solution as I am new to JavaFX
Solution
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:
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 SpringService 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:
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:
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.
Answered By - jewelsea
Answer Checked By - Cary Denson (JavaFixing Admin)