javafx.fxml.LoadException 的未知原因

Unknown cause of javafx.fxml.LoadException

我有一个三阶段的 JavaFX 应用程序(现在只有 login/register/blank 视图),我试图在它们之间进行通信。我创建了一个超级 class,所有阶段都扩展了那个超级 class。 现在,当我 运行 主 class 如下所示时,我得到 运行 时间错误:

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

@Override
public void start(Stage primaryStage) throws Exception {
    conn = DBUtils.connectToDB(DB_SCHEMA);
    new LoginController(primaryStage);
}

异常:

Exception in Application start method
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication(LauncherImpl.java:195)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: javafx.fxml.LoadException: 
/C:/Mihai/Faculta/Anul%203/Licenta/Aplicatie/licenta-mihai-laza/out/production/licenta-mihai-laza/application/login.fxml:7

at javafx.fxml/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2625)
at javafx.fxml/javafx.fxml.FXMLLoader.access0(FXMLLoader.java:105)
at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:941)
at javafx.fxml/javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:980)
at javafx.fxml/javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:227)
at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:752)
at javafx.fxml/javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2722)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2552)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2466)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3237)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3194)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3163)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3136)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3113)
at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:3106)
at licenta.mihai.laza/application.superclasses.StageSuperclass.<init>(StageSuperclass.java:20)
at licenta.mihai.laza/application.LoginController.<init>(LoginController.java:45)
at licenta.mihai.laza/application.Main.start(Main.java:21)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1(LauncherImpl.java:846)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait(PlatformImpl.java:455)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop(WinApplication.java:174)
... 1 more
Caused by: java.lang.InstantiationException: application.LoginController
    at java.base/java.lang.Class.newInstance(Class.java:571)
    at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:936)
    ... 24 more
Caused by: java.lang.NoSuchMethodException: application.LoginController.<init>()
    at java.base/java.lang.Class.getConstructor0(Class.java:3350)
    at java.base/java.lang.Class.newInstance(Class.java:556)
    ... 25 more
[INFO] Connection to database closed
Exception running application application.Main

StageSuperclass.java:

public class StageSuperclass extends Stage {
// Frame window
private Stage stage;
private double xOffset = 0;
private double yOffset = 0;

public StageSuperclass(Stage stage, String fxmlFilePath) throws java.io.IOException {
    this.stage = stage;
    Parent root = FXMLLoader.load(getClass().getResource(fxmlFilePath));
    Scene scene = new Scene(root, Color.TRANSPARENT);
    root.setStyle("-fx-background-color: transparent;");
    this.initStyle(StageStyle.UNDECORATED);
    root.setOnMousePressed(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent mouseEvent) {
            xOffset = mouseEvent.getSceneX();
            yOffset = mouseEvent.getSceneY();
        }
    });

    root.setOnMouseDragged(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent mouseEvent) {
            stage.setX(mouseEvent.getScreenX() - xOffset);
            stage.setY(mouseEvent.getScreenY() - yOffset);
        }
    });
    this.setScene(scene);
    this.show();
}

public Stage getStage() {
    return this.stage;
}

LoginController.java:

public class LoginController extends StageSuperclass {
// Constants
private static final String LOGIN_FXML_PATH = "/application/login.fxml";
private static final String WRONG_CREDENTIALS_MSG = "Datele de logare sunt gresite!";
private static final String BLANK_CREDENTIALS_MSG = "Introduceti username si parola!";

// PageObjects
RegisterController registerPage;

// FXML Objects
@FXML
private Button btnLogin;
@FXML
private Button btnIesire;
@FXML
private TextField tfdUsername;
@FXML
private TextField tfdParola;
@FXML
private Label lblInvalid;
@FXML
private Hyperlink lnkInregistrare;

// Constructor
public LoginController(Stage stage) throws IOException {
    super(stage, LOGIN_FXML_PATH);
}

public void inregistrareLinkOnAction(ActionEvent event) throws IOException {
    new RegisterController(this.getStage());
}

public void loginButtonOnAction(ActionEvent event) throws IOException {
    if (!tfdUsername.getText().isBlank() && !tfdParola.getText().isBlank()) {
        if (validareLogin()) {
            new MainWindowController(this.getStage());
        }
    } else {
        lblInvalid.setText(BLANK_CREDENTIALS_MSG);
    }
}

public void iesireButtonOnAction(ActionEvent event) {
    Stage stage = (Stage) btnIesire.getScene().getWindow();
    stage.close();
}

private boolean validareLogin() {
    List<String> columns = Arrays.asList(COL_USERNAME, COL_PAROLA);
    List<String> whereValues = Arrays.asList(tfdUsername.getText(), tfdParola.getText());
    if (DBUtils.select(columns, columns, whereValues, DBUtils.TABEL_UTILIZATORI.TABLE_NAME, Main.conn).size() == 1) {
        return true;
    } else {
        lblInvalid.setText(WRONG_CREDENTIALS_MSG);
        return false;
    }
}
}

login.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.*?>
<AnchorPane prefHeight="300.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/11.0.1"
            xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.LoginController">
    <AnchorPane prefHeight="60.0" prefWidth="300.0" style="-fx-background-color: #0084b8;">
        <children>
            <Label alignment="CENTER" contentDisplay="CENTER" layoutY="17.0" prefHeight="17.0" prefWidth="300.0"
                   text="LOGIN" textAlignment="CENTER" textFill="WHITE">
                <font>
                    <Font name="System Bold" size="18.0"/>
                </font>
            </Label>
        </children>
    </AnchorPane>
    <Button id="btnLogin" fx:id="btnLogin" layoutX="33.0" layoutY="216.0" mnemonicParsing="false"
            onAction="#loginButtonOnAction" prefHeight="25.0" prefWidth="233.0" style="-fx-background-color: #0084b8;"
            text="Login" textFill="WHITE">
        <font>
            <Font size="13.0"/>
        </font>
    </Button>
    <TextField id="tfdUsername" fx:id="tfdUsername" layoutX="89.0" layoutY="82.0" promptText="User">
        <font>
            <Font size="14.0"/>
        </font>
    </TextField>
    <PasswordField id="tfdParola" fx:id="tfdParola" layoutX="89.0" layoutY="123.0" promptText="Parola">
        <font>
            <Font size="14.0"/>
        </font>
    </PasswordField>
    <Hyperlink id="lnkInregistrare" fx:id="lnkInregistrare" layoutX="118.0" layoutY="153.0"
               onAction="#inregistrareLinkOnAction" text="Nu ai cont? Inregistreaza-te!">
        <font>
            <Font size="11.0"/>
        </font>
    </Hyperlink>
    <Label layoutX="33.0" layoutY="87.0" text="User">
        <font>
            <Font size="14.0"/>
        </font>
    </Label>
    <Label layoutX="32.0" layoutY="128.0" text="Parola">
        <font>
            <Font size="14.0"/>
        </font>
    </Label>
    <Button id="btnCancel" fx:id="btnIesire" layoutX="33.0" layoutY="254.0" mnemonicParsing="false"
            onAction="#iesireButtonOnAction" prefHeight="25.0" prefWidth="233.0" style="-fx-background-color: #0084b8;"
            text="Iesire" textFill="WHITE">
        <font>
            <Font size="13.0"/>
        </font>
    </Button>
    <Label id="lblInvalid" fx:id="lblInvalid" alignment="TOP_CENTER" contentDisplay="CENTER" layoutX="44.0"
           layoutY="194.0" prefHeight="16.0" prefWidth="233.0" textAlignment="CENTER" textFill="RED">
        <font>
            <Font size="11.0"/>
        </font>
    </Label>
</AnchorPane>

你对解决这个异常有什么建议吗?

异常是因为您的FXML文件指定了fx:controller="application.LoginController"。这将导致 FXMLLoader 通过(有效地)调用其无参数构造函数来创建 application.LoginController 的实例。但是,您的 LoginController class 没有无参数构造函数:因此出现异常:

Caused by: java.lang.NoSuchMethodException: application.LoginController.<init>()

典型的方法是加载 FXML 文件,然后让 FXMLLoader 为您创建控制器。除了非常高级的用法,您不应该自己实例化控制器 class,因为 FXMLLoader 会为您完成。

摆脱 LoginController 中的继承(即它不需要是您的其他控制器 class 的子class,当然不应该是子class,共 Stage)。然后做

public class MyApplication extends Application {

    private double xOffset = 0 ;
    private double yOffset = 0 ;

    public static void main(String[] args) {
        launch(args);
    }
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource(LOGIN_FXML_PATH));
        Parent root = loader.load();
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.initStyle(StageStyle.UNDECORATED);
        root.setOnMousePressed(mouseEvent -> {
            xOffset = mouseEvent.getSceneX();
            yOffset = mouseEvent.getSceneY();
        });
    
        root.setOnMouseDragged(mouseEvent ->  {
            stage.setX(mouseEvent.getScreenX() - xOffset);
            stage.setY(mouseEvent.getScreenY() - yOffset);
        });

        primaryStage.show();
    }
}

如果您需要重复使用相同的功能,只需以正常方式创建一个单独的 class:

public class StageDragHandler {

    private double xOffset = 0 ;
    private double yOffset = 0 ;

    public Stage createStage(Parent root) {
        Stage stage = new Stage();
        configureStage(root, stage);
        return stage ;
    }

    public void configureStage(Parent root, Stage stage) {
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.initStyle(StageStyle.UNDECORATED);
        root.setOnMousePressed(mouseEvent -> {
            xOffset = mouseEvent.getSceneX();
            yOffset = mouseEvent.getSceneY();
        });
    
        root.setOnMouseDragged(mouseEvent -> {
            stage.setX(mouseEvent.getScreenX() - xOffset);
            stage.setY(mouseEvent.getScreenY() - yOffset);
        });
    }
}

然后你的申请class减少到:

public class MyApplication extends Application {

    public static void main(String[] args) {
        launch(args);
    }
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource(LOGIN_FXML_PATH));
        Parent root = loader.load();
        new StageDragHandler().configureStage(root, primaryStage);   
        primaryStage.show();
    }
}

并且您可以在任何需要的地方使用类似的代码。