2

I want to combine two libraries - JavaFx and Spring Boot, thus I need to have an instance of ApplicationContext in my start() method in main class:

Main class:

@SpringBootApplication
public class JavaFXandSpringBootTestApplication extends Application{

    @Autowired
    ApplicationContext ctx;

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

    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/LogIn.fxml"));
        loader.setController(ctx.getBean(LogInController.class)); // HERE OCCURES ERROR
        Scene scene = new Scene((Parent)loader.load());
        stage.setScene(scene);
        stage.show();

    }

}

Controller class:

@Component
public class LogInController {

}

As I have read the documentary @SpringBootApplication enables auto detect of Components and under the hood is also annotating the class with @Configuration. So now, why am i reciving a NullPointerException in commended line? It's because that ctx isn't injected properly, but why? What am i doing wrong?

Michael213
  • 317
  • 3
  • 11
  • I can also add that there are many examples in other websites that perform this in the same way! (e.g https://www.mkyong.com/spring-boot/how-to-display-all-beans-loaded-by-spring-boot/) – Michael213 Dec 08 '17 at 14:50
  • How about taking a look at what the [`run`](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/SpringApplication.html#run-java.lang.Object-java.lang.String...-) method returns... Also what is your `launch` method doing looks like this is a JavaFX application which doesn't actually use the `main` in this case. – M. Deinum Dec 08 '17 at 14:51

1 Answers1

3

You can't inject the ApplicationContext (or anything else) into the Application instance on which start() is invoked: that instance of Application is created for you by JavaFX, not by Spring. Since it's not a spring-managed object, there is no mechanism for Spring to inject anything into it.

Move the call to SpringApplication.run() to the init() method, where you can simply assign the context directly. Also, imho a better way to let the controllers be Spring-managed is to tell the FXMLLoader to create controllers via Spring, which you can do with a controller factory. See Dependency Injection and JavaFX.

@SpringBootApplication
public class JavaFXandSpringBootTestApplication extends Application{

    private ApplicationContext ctx;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void init() {
        String[] args = getParameters().getRaw().toArray(new String[0]);
        ctx = SpringApplication.run(JavaFXandSpringBootTestApplication.class, args);
    }

    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/LogIn.fxml"));
        loader.setControllerFactory(ctx::getBean); 
        // or, if you prefer,
        // loader.setController(ctx.getBean(LoginController.class));
        // though the controller factory approach lets you use `fx:controller` as usual.
        Scene scene = new Scene((Parent)loader.load());
        stage.setScene(scene);
        stage.show();

    }

}
James_D
  • 201,275
  • 16
  • 291
  • 322