I'm creating a dashboard for a management system using JavaFX. Also, i'm using an MVC pattern.
I'm having troubles dealing with dependency injection: my main problem was that my HomepageView contains an HomepageController variable, and on the other side HomepageController contains an HomepageView.
So, i tried with DI using constructors, but it would be cyclical so i gave up and tried DI using Setters. So, i have a Main class, and in the start method i instantiate the 2 classes using the default constructor and set each others variables. This is my main code:
public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HomepageView.class.getResource("/homepage/homepage.fxml"));
        Scene scene = new Scene(fxmlLoader.load());
        stage.setTitle("Hello!");
        stage.setScene(scene);
        stage.show();
        HomepageController homeController = new HomepageController();
        HomepageView homeView = new HomepageView();
        PersonalizzaMenuController personalizzaController = new PersonalizzaMenuController();
        PersonalizzaMenuView personalizzaView = new PersonalizzaMenuView();
        
        personalizzaController.setPersonalizzaMenu(personalizzaView);
        personalizzaView.setPersonalizzaMenuController(personalizzaController);
        homeView.setHomepageController(homeController);
        homeController.setHomepageControllers(homeView, personalizzaView);
    }
}
This is my Homepage view class:
 package com.example.ratatouille23.homepage;
import com.example.ratatouille23.Model.Utente;
import com.example.ratatouille23.View;
import com.example.ratatouille23.View2;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.IOException;
public class HomepageView {
    @FXML
    BorderPane borderPane;
    @FXML
    Label labelUsername;
    private Stage stage;
    private Scene scene;
    private Parent root;
    private String nomeNodo = "HomePage";
    private HomepageController homepageController;
    // *********************
    public HomepageView(){};
    @FXML
    public void initialize(){
        labelUsername.setText(Utente.getUsername());
    }
    //************************
    // Action event
    public void clickPulsantePersonalizzaMenu(){
        homepageController.onPersonalizzaMenuClicked();
    }
    @Override
    public Node loadNode() throws IOException {
        return FXMLLoader.load(getClass().getResource("/homepage/homepage.fxml"));
    }
    public void setHomepageController(HomepageController homepageController) {
        this.homepageController = homepageController;
    }
}
And this is my HomepageController class:
package com.example.ratatouille23.homepage;
import com.example.ratatouille23.View;
import com.example.ratatouille23.creaUtente.CreaUtenteView;
import com.example.ratatouille23.inserisciAvvisi.InserisciAvvisiView;
import com.example.ratatouille23.personalizzaMenu.NuovoPiattoView;
import com.example.ratatouille23.personalizzaMenu.PersonalizzaMenuView;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.layout.BorderPane;
import java.io.IOException;
import java.util.Optional;
public class HomepageController {
    HomepageView homepage;
    PersonalizzaMenuView personalizzaMenu;
    //**************************
    // Costruttori
    HomepageController(){};
    //**************************
    // On Action Event
    public void onPersonalizzaMenuClicked() {
           try {
            setBorderPaneCenter(personalizzaMenu.loadNode());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
 
    //**************************
    // Metodi di utility
    public void setBorderPaneCenter(Node node){
        homepage.borderPane.getChildren().remove(homepage.borderPane.getCenter());
        homepage.borderPane.setCenter(node);
    }
    public void setBorderPaneRight(Node node){
       homepage.borderPane.getChildren().remove(homepage.borderPane.getRight());
        homepage.borderPane.setCenter(node);
    }
    public void setHomepageControllers(HomepageView home, PersonalizzaMenuView personalizzamenu){
        this.homepage = home;
        this.personalizzaMenu = personalizzamenu;
       }
}
Homepage FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView?>
<?import java.lang.String?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.text.Font?>
<BorderPane fx:id="borderPane" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.ratatouille23.homepage.HomepageView">
   <left>
      <AnchorPane prefHeight="700.0" prefWidth="306.0" BorderPane.alignment="CENTER">
         <children>
            <AnchorPane layoutX="18.0" layoutY="279.0" prefHeight="421.0" prefWidth="277.0" stylesheets="@homepage.css">
               <styleClass>
                  <String fx:value="nav-2" />
                  <String fx:value="shadow" />
               </styleClass>
               <children>
                  <Button layoutX="19.0" layoutY="21.0" mnemonicParsing="false" onAction="#clickPulsanteInserisciAvvisi" prefHeight="40.0" prefWidth="240.0" styleClass="nav-btn" stylesheets="@homepage.css" text="Inserisci Avvisi" />
                  <Button fx:id="buttonPersonalizzaMenu" layoutX="20.0" layoutY="89.0" mnemonicParsing="false" onAction="#clickPulsantePersonalizzaMenu" prefHeight="40.0" prefWidth="240.0" styleClass="nav-btn" stylesheets="@homepage.css" text="Personalizza Menu" />
                  <Button fx:id="buttonCreaUtente" layoutX="21.0" layoutY="158.0" mnemonicParsing="false" onAction="#clickPulsanteCreaUtente" prefHeight="40.0" prefWidth="240.0" styleClass="nav-btn" stylesheets="@homepage.css" text="Crea Utente" />
                  <Button layoutX="23.0" layoutY="372.0" mnemonicParsing="false" prefHeight="34.0" prefWidth="41.0" styleClass="signout" stylesheets="@homepage.css">
                     <graphic>
                        <FontAwesomeIconView glyphName="SIGN_OUT" size="2em" wrappingWidth="20.861303329467773" />
                     </graphic>
                  </Button>
                  <Label layoutX="75.0" layoutY="372.0" text="Log out">
                     <font>
                        <Font name="Al Nile" size="18.0" />
                     </font>
                  </Label>
               </children>
            </AnchorPane>
            <AnchorPane layoutX="18.0" prefHeight="253.0" prefWidth="277.0" stylesheets="@homepage.css">
               <children>
                  <FontAwesomeIconView fill="WHITE" glyphName="USER" layoutX="116.0" layoutY="63.0" size="60" />
                  <Label layoutX="78.0" layoutY="90.0" text="Benvenuto," textFill="WHITE">
                     <font>
                        <Font name="Tahoma" size="24.0" />
                     </font>
                  </Label>
                  <Label fx:id="labelUsername" layoutX="87.0" layoutY="124.0" text="UTENTE" textFill="WHITE">
                     <font>
                        <Font name="Al Nile" size="24.0" />
                     </font>
                  </Label>
               </children>
               <styleClass>
                  <String fx:value="shadow" />
                  <String fx:value="nav-1" />
               </styleClass>
            </AnchorPane>
         </children>
      </AnchorPane>
   </left>
   <top>
      <AnchorPane prefHeight="41.0" prefWidth="1100.0" BorderPane.alignment="CENTER" />
   </top>
   <right>
      <AnchorPane prefHeight="700.0" prefWidth="343.0" BorderPane.alignment="CENTER" />
   </right>
   <center>
      <AnchorPane maxWidth="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="453.0" stylesheets="@homepage.css" BorderPane.alignment="CENTER" />
   </center>
</BorderPane>
This is PersonalizzaMenuView:
package com.example.ratatouille23.personalizzaMenu;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import java.io.IOException;
import java.util.Optional;
public class PersonalizzaMenuView {
   @FXML
   Accordion listaCategorie;
   PersonalizzaMenuController personalizzaMenuController;
   public PersonalizzaMenuView(){};
   // Carica nodo schermata
   public Node caricaNodoPersonalizzaMenu() throws IOException {
       FXMLLoader loader = new FXMLLoader(getClass().getResource("/personalizzaMenu/personalizza-menu.fxml"));
       return loader.load();
   }
   public void setPersonalizzaMenuController(PersonalizzaMenuController personalizzaMenuController) {
       this.personalizzaMenuController = personalizzaMenuController;
   }
}
PersonalizzaMenuController:
package com.example.ratatouille23.personalizzaMenu;
import javafx.scene.control.Label;
import javafx.scene.control.TextInputDialog;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.AnchorPane;
import java.util.Optional;
public class PersonalizzaMenuController {
   PersonalizzaMenuView personalizzaMenu;
   public PersonalizzaMenuController(PersonalizzaMenuView personalizzamenu){
       this.personalizzaMenu = personalizzamenu;
   }
   public PersonalizzaMenuController(){};
   public void onPulsanteAggiungiCategoriaClicked() {
       //
   }
        */
   }
   public void setPersonalizzaMenu(PersonalizzaMenuView personalizzaMenu) {
       this.personalizzaMenu = personalizzaMenu;
   }
}
PersonalizzaMenu FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.String?>
<?import javafx.scene.control.Accordion?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<BorderPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="682.0" prefWidth="500.0" styleClass="white-bg" stylesheets="@../homepage/homepage.css" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.ratatouille23.personalizzaMenu.PersonalizzaMenuView">
  <bottom>
     <HBox alignment="CENTER" prefHeight="129.0" prefWidth="500.0" spacing="60.0">
        <children>
           <Button mnemonicParsing="false" onAction="#clickPulsantePiu" styleClass="signout" stylesheets="@../homepage/homepage.css" text="+ Aggiungi Categoria" textFill="WHITE">
              <font>
                 <Font name="Tahoma" size="13.0" />
              </font>
           </Button>
           <Button mnemonicParsing="false" onAction="#clickPulsanteAggiungiPiatto" text="+ Aggiungi piatto" />
           <Button mnemonicParsing="false" text="- Elimina" />
        </children>
     </HBox>
  </bottom>
  <top>
     <HBox alignment="CENTER" prefHeight="130.0" prefWidth="500.0" stylesheets="@../homepage/homepage.css" BorderPane.alignment="CENTER">
        <styleClass>
           <String fx:value="nav-1" />
           <String fx:value="shadow" />
        </styleClass>
        <children>
           <Label alignment="TOP_CENTER" contentDisplay="TOP" text="PERSONALIZZA MENU" textAlignment="CENTER" textFill="WHITE">
              <font>
                 <Font name="Tahoma" size="29.0" />
              </font>
           </Label>
        </children>
     </HBox>
  </top>
  <center>
     <ScrollPane fitToHeight="true" fitToWidth="true" hbarPolicy="NEVER" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
        <content>
           <VBox prefHeight="200.0" prefWidth="100.0">
              <children>
                 <Accordion fx:id="listaCategorie">
                   <panes>
                       <TitledPane animated="true" text="untitled">
                          <content>
                             <VBox prefHeight="118.0" prefWidth="462.0" />
                          </content>
                       </TitledPane>
                   </panes>
                 </Accordion>
              </children>
           </VBox>
        </content>
     </ScrollPane>
  </center>
</BorderPane>
When i run the main method, the homepage starts, but whenever i press a button that should load another window this is the error:
mar 21, 2023 5:50:44 PM com.sun.glass.ui.mac.MacApplication lambda$waitForReactivation$6
WARNING: Timeout while waiting for app reactivation
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml@19/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1857)
    at javafx.fxml@19/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1724)
    at javafx.base@19/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base@19/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base@19/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base@19/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics@19/javafx.scene.Node.fireEvent(Node.java:8923)
    at javafx.controls@19/javafx.scene.control.Button.fire(Button.java:203)
    at javafx.controls@19/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:207)
    at javafx.controls@19/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base@19/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
    at javafx.base@19/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base@19/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base@19/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base@19/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base@19/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics@19/javafx.scene.Scene$MouseHandler.process(Scene.java:3894)
    at javafx.graphics@19/javafx.scene.Scene.processMouseEvent(Scene.java:1887)
    at javafx.graphics@19/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2620)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
    at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
    at javafx.graphics@19/com.sun.glass.ui.View.handleMouseEvent(View.java:551)
    at javafx.graphics@19/com.sun.glass.ui.View.notifyMouse(View.java:937)
    at javafx.graphics@19/com.sun.glass.ui.mac.MacView.notifyMouse(MacView.java:127)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:116)
    at java.base/java.lang.reflect.Method.invoke(Method.java:578)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:77)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    at java.base/java.lang.reflect.Method.invoke(Method.java:578)
    at javafx.base@19/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml@19/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:84)
    at javafx.fxml@19/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1854)
    ... 44 more
Caused by: java.lang.NullPointerException: Cannot invoke "com.example.ratatouille23.homepage.HomepageController.onInserisciAvvisiClicked()" because "this.homepageController" is null
    at com.example.ratatouille23/com.example.ratatouille23.homepage.HomepageView.clickPulsanteInserisciAvvisi(HomepageView.java:54)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    ... 51 more
This sounds strange to me, because i used the setter methods, so the HomepageController should not be null.
I hope i gave all the useful informations. I'm also sure i probably not quite understood the right way to use this approach.
