4

I read few spring tutorials with basic examples, and I am a bit confused on how to wire up things properly.

The trouble is, I wanted to use application context to pull singleton controller references, but I read on few other topics that application context should not be accessed directly unless it is absolutely necessary. I think I should use constructor to instantiate reference I want, but here things get all blurry for me.

I have javafx application with several fxml files. I have one main fxml and other are loaded dynamically inside main.

I'll use simplified code for example with two fxml controllers, MainController.java (for main fxml) and ContentController.java (for content fxml)

The idea is that content fxml has TabPane, and main fxml has button which opens new tab in TabPane on ContentController.

I am currently doing something like this

bean xml:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

<bean id="contentController"
class="ContentController"
scope="singleton" />
</beans>

MainControler:

public class MainOverlayControler {

ApplicationContext context;

@FXML
private BorderPane borderPane;

@FXML
private void initialize() {
    loadContentHolder();
}
@FXML
private Button btn;

@FXML
private void btnOnAction(ActionEvent evt) {
    ((ContentController)context.getBean("contentController")).openNewContent();
}

private void loadContentHolder() {
    //set app context
    context = new ClassPathXmlApplicationContext("Beans.xml");

    Node fxmlNode;
    FXMLLoader fxmlLoader = new FXMLLoader();
    fxmlLoader.setController(context.getBean("contentController"));

    try {
    fxmlNode = (Node)fxmlLoader.load(getClass().getResource("Content.fxml").openStream());
    borderPane.setCenter(fxmlNode);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

ContentController:

public class ContentController {
@FXML
private TabPane tabPane;

public void openNewContent() {
    Tab newContentTab = new Tab();
    newContentTab.setText("NewTab");
    tabPane.getTabs().add(newContentTab);
    }
}

Main class:

public class MainFX extends Application {
@Override
public void start(Stage primaryStage) {
    try {
        FXMLLoader fxmlLoader = new FXMLLoader();
        Parent root = (Parent) fxmlLoader.load(getClass().getResource("main.fxml").openStream());
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    } catch(Exception e) {
    e.printStackTrace();
    }
public static void main(String[] args) {
    launch(args);
    }
}

Question: I would like to know how to do same thing with constructor DI, or some other way if I misunderstood the concept.

I need to be able to call "openNewContent" on singleton instance of "ContentController" from multiple other controllers as well.

Miljac
  • 2,045
  • 4
  • 19
  • 28

1 Answers1

4

Read some more on JavaFX and Spring integration. I recommend series by Stephen Chin

Basically, You need to embed your JavaFX classes into Spring context life cycle, and it is very nicely described in linked article.

You can also check his example project on GitHub, where the code is simplified with controller factories introduced in JavaFX 2.1 (check here)

endriu_l
  • 1,649
  • 16
  • 20
  • Is this still state of the art? Or is there anything new in 2020? – wilx Aug 10 '20 at 20:04
  • 1
    @wilx IDK, I guess you did some googling for that. If not - here is one of the first results, I hope it will be useful https://spring.io/blog/2019/01/16/spring-tips-javafx – endriu_l Aug 11 '20 at 22:11