3

I have a question from a tutorial by MVP Integrating Spring Boot with JavaFX

GitHub: https://github.com/mvpjava

Youtube: https://www.youtube.com/watch?v=hjeSOxi3uPg

where there is a public method named initialization() that I don't know how it is invoked after spring boot application starts up. I looked everywhere in the classes and fxml files that it could have a reference to it, and I found none.

I want to know how this method is invoked since I also want to do some initialization of JavaFX controls. I tried using @PostConstruct but this is wrong since all beans are created before any JavaFX controls are created thus I get null pointer exception.

I will be very grateful if someone will enlighten me on this matter.

here is the class at which the public method initialization() I mentioned.

@Component
public class ConsoleTabController {

    @FXML private TextArea missionOverviewText;
    @FXML private ListView<String> missionsList;

    @Autowired @Qualifier("stringPrintWriter")
    private PrintWriter stackTraceWriter;

    @Autowired MissionsService service;
    private TabPaneManger tabManager;

    public void initialize() {
        ObservableList<String> missions = FXCollections.observableArrayList("Apollo", "Shuttle", "Skylab");
        missionsList.setItems(missions);
    }

    @FXML
    private void onMouseClicked(MouseEvent event) {
        missionOverviewText.clear();
        final String selectedItem = missionsList.getSelectionModel().getSelectedItem();
        missionOverviewText.positionCaret(0);
        missionOverviewText.appendText(getInfo(selectedItem));
    }

    @Autowired
    private void setTabManager(TabPaneManger tabManager){
        this.tabManager = tabManager;
    }

    public String getInfo(String selectedItem) {
        String missionInfo = null ;

        try {
            missionInfo = service.getMissionInfo(selectedItem); 
            getLog().appendText("Sucessfully retrieved mission info for " + selectedItem + "\n");
        } catch (IOException exception) {
            exception.printStackTrace (stackTraceWriter);
            getLog().appendText(stackTraceWriter.toString() + "\n");
        }
        return missionInfo;
    }

    public TextArea getMissionOverviewText() {
        return missionOverviewText;
    }

    public ListView<String> getMissionsList() {
        return missionsList;
    }

    private TextArea getLog(){
        return tabManager.getVisualLog();
    }
}
  • Please have a look at https://stackoverflow.com/questions/44010909/using-initialize-method-in-a-controller-in-fxml – alirabiee Sep 02 '17 at 15:32

1 Answers1

7

The initialize() method (which is what I think you mean) is invoked by the FXMLLoader. In general, for an FXMLLoader, the order of execution is:

  1. FXMLLoader loads the FXML file and parses it
  2. If the root element in the FXML file has a fx:controller attribute, it gets a reference to an instance of that class; it does this by passing the controller class to the controllerFactory, if one is set, or by calling the controller class's default constructor otherwise.
  3. Any elements with fx:id attributes are injected into the controller fields with matching @FXML annotations
  4. The FXMLLoader invokes the controller's initialize() method, if it has one.

In your case, I assume you are setting the controller factory of the FXMLLoader to delegate to the Spring application context, i.e. I assume you have something like

ApplicationContext appContext = ... ; // Spring bean factory
FXMLLoader loader = new FXMLLoader();
loader.setLocation(...);
loader.setControllerFactory(appContext::getBean);
Parent ui = loader.load();

This means that the controller instances will be created by passing the controller class to the Spring bean factory's getBean(...) method. So if the FXML file has fx:controller="ConsoleTabController", the FXMLLoader essentially calls

Object controller = appContext.getBean(ConsoleTabController.class);

in step 2 above. The Spring application context creates a ConsoleTabController instance (assuming you have configured the controller bean as having prototype scope, which you should), injects any @AutoWired-annotated properties, calls any @PostConstruct-annotated methods, and then provides the controller to the FXMLLoader. So the overall order of execution when you use the Spring bean factory as the controller factory is

  1. FXMLLoader loads the FXML file and parses it
  2. If the root element in the FXML file has a fx:controller attribute:
    1. The Spring bean factory creates an instance of the controller class
    2. The Spring bean factory injects any @Autowired-annotated properties into the controller instance
    3. The Spring bean factory invokes any @PostConstruct-annotated methods on the controller instance
    4. The controller instance is returned to the FXMLLoader
  3. Any elements with fx:id attributes are injected into the controller fields with matching @FXML annotations by the FXMLLoader
  4. The FXMLLoader invokes the controller's initialize() method, if it has one.

Note that there are essentially two different kind of injected fields, and two different kinds of "initialization" methods in this scenario. The "Spring injected fields", annotated @Autowired (or @Inject) are injected first, then the "Spring initialization methods" (annotated @PostConstruct) are invoked. After that, the "FXML injected fields" (annotated @FXML) are injected, and then the "FXML initialization method" (the one called initialize()) is invoked. You don't have any @PostConstruct methods in your sample code, but the one thing to be careful of is that those methods would be invoked before the @FXML-annotated fields are initialized.

James_D
  • 201,275
  • 16
  • 291
  • 322
  • Yes thank you very much. Now I know who is calling the initialize() method, it is the `FXMLLoader`. I tried to replicate the tutorial pattern into my project and that is where I stumbled, I forgot to include the fxml file its **controller class** when I am testing that is why the `initialize()` method is never invoked. So I tried the `@PostConstruct` annotation instead and I got `NullPointerException` which you also explained and too how did i got such error. Thank you very much for step by step explanation. – Victor Harlan Lacson Sep 03 '17 at 09:13