Spring不会为JavaFx视图加载控制器类

时间:2017-05-16 17:26:16

标签: java spring javafx spring-boot

我目前正在开发一个项目,我正在使用Spring和JavaFX,我遇到了一个问题。 我最初有一个登录框,在用户按下登录按钮后,必须出现另一个框架。问题是,在我构建我的第二个FXML文件(第二帧)并尝试将其链接到其控制器之后,Spring将不会出于某种原因进行此链接。我用@Component注释了我的控制器类,但确实有所不同。如果我没有设置我的fx:controller(保持为空),框架会成功加载。我在这个问题上花了最后6-7个小时,但我没有找到任何满意的答案。

我的控制器类如下所示:

package project.eHealth;

import java.net.URL;
import java.util.ResourceBundle;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import project.eHealth.bussiness.AppointmentDto;
import project.eHealth.bussiness.AppointmentService;

@Component
public class DoctorViewController {

    @FXML
    private TableView<AppointmentDto> appointmentTable;

    @FXML
    private TableColumn<AppointmentDto,String> doctorNameColumn ;

    @FXML
    private TableColumn<AppointmentDto,String> patientNameColumn ;


    @Autowired
    private AppointmentService appointments;

    @FXML
    public void initialize(){
        patientNameColumn.setCellValueFactory(cellData -> cellData.getValue().getPatientName());
    }

    @FXML
    public TableView<AppointmentDto> getAppointmentTable() {
        return appointmentTable;
    }


    public void setAppointmentTable(TableView<AppointmentDto> appointmentTable) {
        this.appointmentTable = appointmentTable;
    }


    @FXML
    public TableColumn<AppointmentDto, String> getDoctorNameColumn() {
        return doctorNameColumn;
    }


    public void setDoctorNameColumn(TableColumn<AppointmentDto, String> doctorNameColumn) {
        this.doctorNameColumn = doctorNameColumn;
    }

    @FXML
    public TableColumn<AppointmentDto, String> getPatientNameColumn() {
        return patientNameColumn;
    }


    public void setPatientNameColumn(TableColumn<AppointmentDto, String> patientNameColumn) {
        this.patientNameColumn = patientNameColumn;
    }


    @FXML
    public AppointmentService getAppointments() {
        return appointments;
    }


    public void setAppointments(AppointmentService appointments) {
        this.appointments = appointments;
    }

}

相应的FXML文件如下:

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="project.eHealth.DoctorViewController">
   <children>
      <SplitPane dividerPositions="0.4882943143812709" layoutX="148.0" layoutY="71.0" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
        <items>
          <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
               <children>
                  <TableView fx:id="appointmentTable" layoutX="14.0" layoutY="60.0" prefHeight="398.0" prefWidth="289.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
                    <columns>
                      <TableColumn fx:id="doctorNameColumn" prefWidth="95.0" text="Patient Name" />
                      <TableColumn fx:id="patientNameColumn" prefWidth="105.0" text="Date Issued" />
                    </columns>
                     <columnResizePolicy>
                        <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
                     </columnResizePolicy>
                  </TableView>
               </children>
            </AnchorPane>
          <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
               <children>
                  <Label layoutX="23.0" layoutY="21.0" text="Patient name" />
                  <Label layoutX="30.0" layoutY="99.0" prefHeight="17.0" prefWidth="70.0" text="Date Issued" />
                  <Button layoutX="172.0" layoutY="227.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="111.0" text="Accept" />
                  <Button layoutX="172.0" layoutY="277.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="111.0" text="Reject" />
               </children>
            </AnchorPane>
        </items>
      </SplitPane>
   </children>
</AnchorPane>

主要应用类:

package project.eHealth;

import java.io.IOException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;



@SpringBootApplication
public class EHealthApplication extends Application {
    private ConfigurableApplicationContext context;
    private Parent rootNode;
    private Stage primary;
    private Stage doctorStage;

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

    @Override
    public void init() throws Exception {
        context = SpringApplication.run(EHealthApplication.class);
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Main.fxml"));
        fxmlLoader.setControllerFactory(context::getBean);
        rootNode = fxmlLoader.load();
}

    @Override
    public void start(Stage primaryStage) throws Exception {
        this.primary = primaryStage;
        primaryStage.setScene(new Scene(rootNode));
        primaryStage.setTitle("E-Health Login");
        primaryStage.show();


    }

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

    @FXML
    void showDoctorFrame() throws IOException{
        System.out.println("Handle regular User");
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(EHealthApplication.class.getResource("DoctorView.fxml"));
        loader.setControllerFactory(context::getBean);
        rootNode = loader.load();

        doctorStage = new Stage();
        doctorStage.setTitle("PatientView");
        Scene scene = new Scene(rootNode);
        doctorStage.setScene(scene);
        doctorStage.show();
    }

}

按下登录按钮后执行的方法是 showDoctorFrame()

堆栈跟踪是:

Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1774)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Node.fireEvent(Node.java:8413)
    at javafx.scene.control.Button.fire(Button.java:185)
    at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:381)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:417)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:416)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
    at com.sun.glass.ui.View.notifyMouse(View.java:937)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
    at sun.reflect.GeneratedMethodAccessor15.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1771)
    ... 48 more
Caused by: javafx.fxml.LoadException: 
/C:/Users/Dragos/Desktop/eHealth/target/classes/project/eHealth/DoctorView.fxml:8

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409)
    at project.eHealth.EHealthApplication.showDoctorFrame(EHealthApplication.java:60)
    ... 58 more
Caused by: java.lang.NullPointerException
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:929)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
    ... 61 more

非常感谢任何形式的帮助!

1 个答案:

答案 0 :(得分:0)

看起来您正在使用与Application类相同的类以及主fxml文件的控制器类。这通常是一个坏主意。

Application.launch()(大约)创建Application类的实例,启动FX工具包,在创建的实例上调用init(),创建主要阶段,然后调用{{1在FX应用程序线程上创建的实例上。请注意,在您的情况下,Spring不管理此实例:它是在spring bean工厂外部创建的。

如果将此类指定为FXML文件的控制器,则FXML加载程序将&#34;创建&#34;加载FXML文件时控制器类的实例。请注意,在这种情况下,由于您为FXML加载程序指定了一个控制器工厂,它将通过调用控制器工厂来创建此实例,因此它将获得&#34;控制器实例&#34;来自春豆工厂的班级。但是,这将是start()的不同实例,而不是EHealthApplication创建的实例。

Application.launch()中的context字段已在EHealthApplication方法中初始化。在init()创建的实例上调用此方法。因此,Application.launch()永远不会在&#34;控制器实例中初始化&#34;这是从spring bean工厂获得的(因为context没有在该实例上调用)。因此,当调用事件处理程序方法init()时,showDoctorFrame()为空,并且当FXML Loader尝试在医生视图FXML中处理context属性时,您将获得空指针异常。 / p>

您应该为主控制器使用单独的类。由于fx:controller是一个众所周知的Spring对象&#34;,你可以将它直接注入(Spring-managed)控制器:

ApplicationContext

然后

package project.eHealth;

import java.io.IOException;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;



@SpringBootApplication
public class EHealthApplication extends Application {
    private ConfigurableApplicationContext context;
    private Parent rootNode;
    private Stage primary;

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

    @Override
    public void init() throws Exception {
        context = SpringApplication.run(EHealthApplication.class);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {

        // UI work should really be here, as it's on the FX Application Thread
        // (though I think you can get away with it in the init() method)
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Main.fxml"));
        fxmlLoader.setControllerFactory(context::getBean);
        rootNode = fxmlLoader.load();
        this.primary = primaryStage;
        primaryStage.setScene(new Scene(rootNode));
        primaryStage.setTitle("E-Health Login");
        primaryStage.show();


    }

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



}

并更改主fxml文件中的pacakge project.eHealth ; // imports omitted... @Component public class MainController { @Autowired private ApplicationContext context ; private Stage doctorStage; @FXML void showDoctorFrame() throws IOException{ System.out.println("Handle regular User"); FXMLLoader loader = new FXMLLoader(); loader.setLocation(EHealthApplication.class.getResource("DoctorView.fxml")); loader.setControllerFactory(context::getBean); Parent rootNode = loader.load(); doctorStage = new Stage(); doctorStage.setTitle("PatientView"); Scene scene = new Scene(rootNode); doctorStage.setScene(scene); doctorStage.show(); } } 属性以指向此新类。