我的JavaFX应用程序中有一种列表.所述应用程序基于Spring Boot框架.

我想用用.fxml创建的我自己的JavaFX对象的多个实例填充该列表. 为此,我目前正在为列表中的每一项加载.fxml

FXMLLoader fxmlLoader = new FXMLLoader(PATH_TO_FXML);
fxmlLoader.setControllerFactory(applicationContext::getBean);

VBox listItem = fxmlLoader.load();
contentArea.getChildren().add(listItem);

但这将导致所有项目共享同一个控制器,不是吗? 与其他控制器一样,所述控制器当前仅在初始化后使用@Component进行注释. 有没有办法让Spring在每次收到请求时都创建该控制器的新实例?

或者,有没有更好的方法来实现我的这个 idea ?

spring 版:3.2.1 JavaFX版本:19.0.2 Maven项目

如果有更多的问题,请告诉我.

推荐答案

正如你所猜测的那样

但这将导致所有项目共享同一个控制器,不是吗?

默认情况下,Spring for each Bean创建一个实例,并在对该Bean发出请求时共享相同的实例.这个行为由Springscopes控制,这个默认作用域是singleton作用域.要在每个请求上创建一个新的Bean类实例,您需要prototype作用域.

您可以(也确实应该这样做,因为每次加载FXML时都应该创建一次JavaFX控制器)通过注释控制器类@Scope("prototype")告诉Spring在每次请求控制器时创建一个新实例:

@Component
@Scope("prototype")
public class MyControllerClass {
   // ...
}

这里有一个非常快速的例子.这是一个FXML,我称它为Adder.fxml.它包含两个Spinner<Integer>和一个HBox中的Label.其 idea 是标签将在微处理器中显示这两个值的总和.

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Spinner?>
<?import javafx.scene.layout.HBox?>
<HBox spacing="20.0" xmlns:fx="http://javafx.com/fxml"
      fx:controller="org.jamesd.examples.springscope.AddingController">
    <padding>
        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
    </padding>

    <Spinner fx:id="firstSummand" ></Spinner>
    <Spinner fx:id="secondSummand"></Spinner>
    <Label fx:id="sumText" HBox.hgrow="ALWAYS"/>
</HBox>

这是控制器.请注意,它是否同时带有@Component@Scope("prototytpe")的注释.

package org.jamesd.examples.springscope;

import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
@Scope("prototype")
public class AddingController {
    @FXML
    private Label sumText;
    @FXML
    private Spinner<Integer> firstSummand;
    @FXML
    private Spinner<Integer> secondSummand;

    @FXML
    private void initialize() {
        List.of(firstSummand, secondSummand).forEach(spinner -> {
            spinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(-20, 20, 0));
            spinner.valueProperty().addListener(obs -> updateSum());
        });
        updateSum();
    }

    @FXML
    private void updateSum() {
        sumText.setText(Integer.toString(firstSummand.getValue() + secondSummand.getValue()));
    }
}

下面是一个Spring bootstrap 应用程序类,旨在启动一个JavaFX应用程序:

package org.jamesd.examples.springscope;

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

@SpringBootApplication
public class SpringApp {
    public static void main(String[] args) {
        Application.launch(HelloApplication.class, args);
    }
}

和FX应用程序:

package org.jamesd.examples.springscope;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.IOException;

public class HelloApplication extends Application {

    private ConfigurableApplicationContext applicationContext;

    @Override
    public void init() {
        applicationContext = new SpringApplicationBuilder(SpringApp.class).run();
    }
    @Override
    public void start(Stage stage) throws IOException {

        VBox root = new VBox(10);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(20));
        for (int i = 0 ; i < 3; i++) {
            root.getChildren().add(createAdder());
        }
        Scene scene = new Scene(root, 600, 375);
        stage.setTitle("Adding");
        stage.setScene(scene);
        stage.show();
    }

    private Node createAdder() throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("Adder.fxml"));
        fxmlLoader.setControllerFactory(applicationContext::getBean);
        return fxmlLoader.load();
    }

}

该应用程序创建在Adder.fxml中定义的三个UI实例,它们独立工作.

如果您删除控制器中的@Scope("prototype")注释,您将看到它无法正常工作.(由于每次加载FXML时字段都被重新分配给同一个控制器实例,因此只有最后加载的实例才会更新.)


注您还可以使用Springmeta-annotations来创建您自己的元注释,它表示@Component,它始终包含@Scope("prototype"):

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@Scope("prototype")
public @interface FXController {}

然后你就可以做

@FXController
public class AddingController {
    // ...
}

这可能是值得的,因为spring管理的应用程序中的FX控制器应该是原型范围.

Java相关问答推荐

那么比较似乎不是词典学的,尽管doctor 这么说

Javascript更新alert 可扩展内容样式与CSS—按钮更多/更少

使用包私有构造函数强制子类Java类

Jlink选项&-strie-ative-Commands";的作用是什么?

DTO到实体,反之亦然,控制器和服务之间的哪一层应该处理转换?

FALSE:它应该在什么时候使用?

通过移动一个类解决了潜在的StubbingProblem.它怎麽工作?

如何在 spring 数据的MongoDB派生查询方法中使用$EXISTS

在macOS上读取文件会导致FileNotFound,即使文件存在(并且具有权限)

如何用内置Java从JavaFX应用程序中生成.exe文件?

将关闭拍卖的TimerService

Spring Framework6.1中引入的新RestClient是否有适合于测试的变体,就像RestTemplate和TestRestTemplate一样?

AWS Java SDK v2.x中没有setObjectAcl方法

为什么这种递归会有这样的行为?

如何调整JButton的大小以适应图标?

Java KeyListener不工作或被添加

在Java Spring JPA中插入包含对其他实体的引用的列

从字节数组切换到JakartaMail org.springframework.mail.javamail.JavaMailSender InputStreamResource

如何使用我的RLE程序解决此问题

Spring Integration SFTP 连接失败 - 无法协商 kex 算法的密钥交换