我目前正在开发一个项目,我正在使用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
非常感谢任何形式的帮助!
答案 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();
}
}
属性以指向此新类。