javaspringjavafxnull

how to configure javafx with springboot with more than 1 window?


I have problem. This is my main:

    @SpringBootApplication
public class MyAppSpringApplication extends Application {

    public static ConfigurableApplicationContext springContext;
    private FXMLLoader fxmlLoader;


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

    @Override
    public void start(Stage stage) throws Exception {
        fxmlLoader.setLocation(getClass().getResource("/sample.fxml"));
        Parent root = fxmlLoader.load();
        stage.setTitle("Sample app");
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

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

    @Override
    public void init() throws Exception {
        springContext = SpringApplication.run(MyAppSpringApplication.class);
        fxmlLoader = new FXMLLoader();
        fxmlLoader.setControllerFactory(springContext::getBean);
    }
}

And my first window (sample.fxml) with samplecontroller and sampleservice works ok. But i create another dish-builder.fxml with their contoller and service, but when i try to use my service there, it doesnt work because of null in dishbuilderservice (albo doesnt work sampleservice in that new controller). I heard that i shound also use that:

public static ConfigurableApplicationContext springContext;

but i have no idea how should i use it. Sorry for my weak knowledge and english.

     @Controller
    public class DishBuilderController implements Initializable {
    
        @Autowired
        DishBuilderService dishBuilderService;
    
        @Autowired
        SampleService sampleService;
    
        private void somefun(){
            sampleService.somefunInService(); //here sampleService and 
        every other service has null.

}

Here is the moment when i open new dishBuilder window (its in SampleController):

@FXML
    void addNoweOknoClicked(ActionEvent event) {
        try {
            Stage stage = (Stage)anchorPane.getScene().getWindow();
            FXMLLoader fxmlLoader = new FXMLLoader();
            fxmlLoader.setLocation(getClass().getResource("/dish-builder.fxml"));
            AnchorPane root = fxmlLoader.load();
            stage.setTitle("Sample app");
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.show();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

Solution

  • When you load dish-builder.fxml you are not setting the controller factory on the FXMLLoader. This means the FXMLLoader is simply creating the controller by calling its no-arg constructor. Since the controller is not a spring-managed bean, spring cannot inject any components into it.

    You need to set the controller factory, as you do when you load sample.fxml, so that the FXMLLoader will ask Spring to retrieve the controller from the application context.

    A couple of points that are not strictly relevant to your question:

    So you need:

    @SpringBootApplication
    public class MyAppSpringApplication extends Application {
    
        private ConfigurableApplicationContext springContext;
    
    
        public static void main(String[] args) {
            Application.launch(args);
        }
    
        @Override
        public void start(Stage stage) throws Exception {
            FXMLLoader fxmlLoader = new FXMLLoader();
            fxmlLoader.setControllerFactory(springContext::getBean);
            fxmlLoader.setLocation(getClass().getResource("/sample.fxml"));
            Parent root = fxmlLoader.load();
            stage.setTitle("Sample app");
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.show();
        }
    
        @Override
        public void stop() throws Exception {
            springContext.stop();
        }
    
        @Override
        public void init() throws Exception {
            springContext = SpringApplication.run(MyAppSpringApplication.class);
        }
    }
    

    Then your SampleController should look like

    @Component
    @Scope("prototype")
    public class SampleController {
    
        @Autowired
        private ConfigurableApplicationContext springContext ;
    
        @FXML
        void addNoweOknoClicked(ActionEvent event) {
            try {
                Stage stage = (Stage)anchorPane.getScene().getWindow();
                FXMLLoader fxmlLoader = new FXMLLoader();
                fxmlLoader.setControllerFactory(springContext::getBean);
                fxmlLoader.setLocation(getClass().getResource("/dish-builder.fxml"));
                AnchorPane root = fxmlLoader.load();
                stage.setTitle("Sample app");
                Scene scene = new Scene(root);
                stage.setScene(scene);
                stage.show();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
    

    and similarly

    @Component
    @Scope("prototype")
    public class DishBuilderController implements Initializable {
    
        @Autowired
        DishBuilderService dishBuilderService;
    
        @Autowired
        SampleService sampleService;
    
        private void somefun(){
            // this should now work, since the controller is managed by Spring:
            sampleService.somefunInService();   
        }
    
    }