使用 JavaFX 的依赖注入会导致 NullPointerExceptions

Dependency injection with JavaFX gives NullPointerExceptions

我想混合使用 Java 外汇和 Spring 数据。我找到了有关此问题的视频 https://www.youtube.com/watch?v=u0dEf-QN-90&t=3s 并在我的代码中实现了它。因此,我的 UI 可以正常工作,但无法将依赖项注入 TaskRepository。我一直在尝试以不同的方式注入任何东西,但总是遇到同样的错误,即我的依赖项为空。我做错了什么?

addingWindow.fxml

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>

<HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="pl.projektyjava.listToDoJavaFX.fxmlcontrollers.AddingWindowController">
   <children>
      <VBox prefHeight="400.0" prefWidth="473.0" HBox.hgrow="ALWAYS">
         <children>
            <Label alignment="TOP_CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" text="Tytuł" textAlignment="CENTER" />
            <TextField fx:id="title" VBox.vgrow="ALWAYS" />
            <Label alignment="TOP_CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" text="Opis" textAlignment="CENTER" />
            <TextArea fx:id="description" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS" />
            <HBox maxHeight="-Infinity" VBox.vgrow="ALWAYS">
               <children>
                  <Button fx:id="listOfQuest" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Lista Zadań" HBox.hgrow="ALWAYS" />
                  <Button fx:id="addQuest" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Dodaj zadanie" HBox.hgrow="ALWAYS" />
               </children>
            </HBox>
         </children>
      </VBox>
      <Slider fx:id="priority" blockIncrement="1.0" majorTickUnit="1.0" max="5.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minorTickCount="0" orientation="VERTICAL" showTickLabels="true" showTickMarks="true" snapToTicks="true" HBox.hgrow="ALWAYS" />
   </children>
</HBox>

pl.projektyjava.listToDoJavaFX.appinitialization。初始化

package pl.projektyjava.listToDoJavaFX.appinitialization;

import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class Initialization implements ApplicationListener<StarterClass.StageReadyEvent>{




    @Override
    public void onApplicationEvent(StarterClass.StageReadyEvent event) {
        Stage stage = event.getStage();
        HBox mainPane= null;
        try {
            mainPane = FXMLLoader.load(getClass().getResource("/addingWindow.fxml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        Scene scene=new Scene(mainPane);
        stage.setScene(scene);
        stage.show();
    }
}

pl.projektyjava.listToDoJavaFX.appinitialization.ListToDoJavaFXApplication

package pl.projektyjava.listToDoJavaFX.appinitialization;


import javafx.application.Application;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ListToDoJavaFxApplication  {

    public static void main(String[] args) {
        Application.launch(StarterClass.class, args);

    }
}

pl.projektyjava.listToDoJavaFX.appinitialization.StarterClass

package pl.projektyjava.listToDoJavaFX.appinitialization;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class StarterClass extends Application {

    private ConfigurableApplicationContext applicationContext;

    @Override
    public void init()  {


        applicationContext=new SpringApplicationBuilder(ListToDoJavaFxApplication.class).run();
    }

    @Override
    public void start(Stage stage)  {

        applicationContext.publishEvent(new StageReadyEvent(stage));

    }

    @Override
    public void stop()  {
        applicationContext.close();
        Platform.exit();
    }

    static class StageReadyEvent extends ApplicationEvent {
        public StageReadyEvent (Stage stage){
            super(stage);
        }
        public Stage getStage(){
           return ((Stage) getSource());
        }
    }
}

pl.projektyjava.listToDoJavaFX.body.Task

package pl.projektyjava.listToDoJavaFX.body;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Task {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
    private String description;
    private double priority;

    public Task(String title, String description, double priority) {
        this.title = title;
        this.description = description;
        this.priority = priority;
    }

    public Task() {
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public double getPriority() {
        return priority;
    }

    public void setPriority(double priority) {
        this.priority = priority;
    }
}

pl.projektyjava.listToDoJavaFX.body.Task存储库

package pl.projektyjava.listToDoJavaFX.body;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface TaskRepository extends CrudRepository<Task, Long> {
}

pl.projektyjava.listToDoJavaFX.fxmlcontrollers.AddingWindowController

package pl.projektyjava.listToDoJavaFX.fxmlcontrollers;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Slider;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import pl.projektyjava.listToDoJavaFX.body.Task;
import pl.projektyjava.listToDoJavaFX.body.TaskRepository;

@Controller
public class AddingWindowController {

    @Autowired
    TaskRepository taskRepository;

    @FXML
    private TextField title;

    @FXML
    private TextArea description;

    @FXML
    private Button listOfQuest;

    @FXML
    private Button addQuest;

    @FXML
    private Slider priority;

    public void initialize() {
        addQuest.setOnAction(event -> {
            String titleText = title.getText();
            String descriptionText = description.getText();
            double priorityVal = priority.getValue();

            Task task = new Task(titleText, descriptionText, priorityVal);
            taskRepository.save(task);

        });
    }

}

和错误:

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Cannot invoke "pl.projektyjava.listToDoJavaFX.body.TaskRepository.save(Object)" because "this.taskRepository" is null
    at pl.projektyjava.listToDoJavaFX.fxmlcontrollers.AddingWindowController.lambda$initialize[=17=](AddingWindowController.java:41)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    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:8792)
    at javafx.scene.control.Button.fire(Button.java:203)
    at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:208)
    at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    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:3861)
    at javafx.scene.Scene.processMouseEvent(Scene.java:1854)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2587)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:409)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:299)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent(GlassViewEventHandler.java:447)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:413)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:446)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:556)
    at com.sun.glass.ui.View.notifyMouse(View.java:942)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$runLoop(WinApplication.java:174)

ControlerFactory 添加到 FXMLLoader 之后,我有这个:

javafx.fxml.LoadException: 
/B:/Projekty/1%20PROJEKT/listToDo-JavaFX/listToDo-JavaFX/target/classes/addingWindow.fxml:11

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2707)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2685)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2517)
    at pl.projektyjava.listToDoJavaFX.appinitialization.Initialization.onApplicationEvent(Initialization.java:28)
    at pl.projektyjava.listToDoJavaFX.appinitialization.Initialization.onApplicationEvent(Initialization.java:14)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421)
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378)
    at pl.projektyjava.listToDoJavaFX.appinitialization.StarterClass.start(StarterClass.java:26)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1(LauncherImpl.java:849)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait(PlatformImpl.java:474)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:447)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:446)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$runLoop(WinApplication.java:174)
    at java.base/java.lang.Thread.run(Thread.java:831)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'pl.projektyjava.listToDoJavaFX.fxmlcontrollers.AddingWindowController' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:940)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:982)
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:229)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:754)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2808)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2634)
    ... 19 more
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
2021-04-23 19:47:00.791  INFO 12928 --- [lication Thread] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-04-23 19:47:00.794  INFO 12928 --- [lication Thread] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2021-04-23 19:47:00.800  INFO 12928 --- [lication Thread] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:903)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication(LauncherImpl.java:198)
    at java.base/java.lang.Thread.run(Thread.java:831)
Caused by: java.lang.NullPointerException: Root cannot be null
    at javafx.scene.Scene.<init>(Scene.java:345)
    at javafx.scene.Scene.<init>(Scene.java:207)
    at pl.projektyjava.listToDoJavaFX.appinitialization.Initialization.onApplicationEvent(Initialization.java:32)
    at pl.projektyjava.listToDoJavaFX.appinitialization.Initialization.onApplicationEvent(Initialization.java:14)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421)
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378)
    at pl.projektyjava.listToDoJavaFX.appinitialization.StarterClass.start(StarterClass.java:26)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1(LauncherImpl.java:849)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait(PlatformImpl.java:474)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:447)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:446)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$runLoop(WinApplication.java:174)
    ... 1 more

Process finished with exit code 1

默认情况下,@SpringBootApplication 会在其自己的文件夹和下方进行组件扫描。因此,只需将您的 ListToDoJavaFxApplication 一个文件夹放入 pl.projektyjava.listToDoJavaFX

或者只需在 @SpringBootApplication 的正上方添加 @ComponentScan(“pl.projektyjava.listToDoJavaFX”) 显式定义组件扫描。

通常,只要自动连接在启动时无提示地失败且没有 BeanCreationException,这表明组件扫描未找到您的组件

默认情况下,FXMLLoader 将通过调用无参数构造函数来创建控制器实例。因此,创建的 AddingWindowController 实例不受 Spring 管理,并且 Spring 无法将 @Autowired 依赖项注入其中。 (基本上,Spring ApplicationContext 不知道 AddingWindowController 实例存在。)

您可以向 FXMLLoader 提供 ControllerFactory,它要求控制器由 spring 应用程序上下文创建:

@Component
public class Initialization implements ApplicationListener<StarterClass.StageReadyEvent>{


    @Autowired
    private ApplicationContext applicationContext ;

    @Override
    public void onApplicationEvent(StarterClass.StageReadyEvent event) {
        Stage stage = event.getStage();
        HBox mainPane= null;
        try {
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/addingWindow.fxml"));
            fxmlLoader.setControllerFactory(applicationContext::getBean);
            mainPane = fxmlLoader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Scene scene=new Scene(mainPane);
        stage.setScene(scene);
        stage.show();
    }
}

另请注意,每次加载 FXML 时都应创建一个新的控制器实例,因此 JavaFX 控制器应具有原型作用域。这可能无关紧要,但 Spring 中的 @Controller 注释实际上是为 Web 应用程序中的 MVC 控制器设计的,因此 @Component 构造型在这里更合适:

@Component
@Scope("prototype")
public class AddingWindowController {

    // ...

}