当前位置: 首页 > 知识库问答 >
问题:

Spring与FXML嵌套控制器的集成

孙和安
2023-03-14

使用控制器工厂的问题:

loader.setControllerFactory(new Callback<Class<?>, Object>() {
    @Override
    public Object call(Class<?> param) {
        // OK but doesn't work when multiple instances controller of same type
        return context.getBean(param);
    }
});
  • 到目前为止,我最好的方法是使用Stephen Chin方法连接顶层控制器。
    • 对于有多个bean实例的情况,父控制器将通过@autowire/@qualifer获取对特定bean的引用,然后在相应的控制器上设置。
    • 下一级控制器也可以通过在顶层控制器上公开它们并调用autowire()
    • 来连接
    • 有没有使用控制器工厂机制,这样我就可以在spring上下文中定义控制器,以便更容易地将它们连接起来?
    • 还有其他方法吗?
    <context:annotation-config />
    <bean id="modelA" class="org.example.Model">
        <property name="value" value="a value" />
    </bean>
    <bean id="modelB" class="org.example.Model">
        <property name="value" value="b value" />
    </bean>
    
    <HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.TopLevelController">
       <children>
          <TabPane tabClosingPolicy="UNAVAILABLE">
            <tabs>
              <Tab text="A">
                   <content>
                      <fx:include fx:id="a" source="nested.fxml" />
                   </content>
              </Tab>
              <Tab text="B">
                   <content>
                      <fx:include fx:id="b" source="nested.fxml" />
                   </content>
              </Tab>
            </tabs>
          </TabPane>
       </children>
    </HBox>
    
    <HBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.example.NestedController">
       <children>
          <TextField fx:id="value" />
       </children>
    </HBox>
    
    public class NestedControllersSpring extends Application {
        public static void main(String[] args) {
            launch(args);
        }
        @Override
        public void start(Stage stage) throws Exception {
    
            ClassPathXmlApplicationContext context =
                    new ClassPathXmlApplicationContext("/app-context.xml");
    
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/top-level.fxml"));
            stage.setScene(new Scene(loader.load()));
            TopLevelController top = loader.getController();
            context.getAutowireCapableBeanFactory().autowireBean(top);
            context.getAutowireCapableBeanFactory().autowireBean(top.getAController());
            context.getAutowireCapableBeanFactory().autowireBean(top.getBController());
            top.init(); // needed because autowire doesn't call @PostConstruct
            stage.show();
        }
    }
    

    顶层控制器

    public class TopLevelController {
    
        @FXML
        private NestedController aController;
    
        @FXML
        private NestedController bController;
    
        @Autowired
        @Qualifier("modelA")
        private Model a;
    
        @Autowired
        @Qualifier("modelB")
        private Model b;
    
        @PostConstruct
        public void init() {
            aController.setModel(a);
            bController.setModel(b);
        }
        public NestedController getAController() {
            return aController;
        }
        public NestedController getBController() {
            return bController;
        }
    }
    

共有1个答案

洪安顺
2023-03-14

我看不出有什么办法能完全干净利落地做到这一点。正如您所注意到的,控制器工厂获得的唯一信息是控制器类,因此无法区分实例。您可以使用FXML为这些控制器提供更多的值,但是这些值将在FXML初始化阶段设置,这将发生在Spring初始化阶段之后。(我认为这就是@PostConstruct不能工作的原因:@PostConstruct方法将在构造之后调用,但在FXML注入之前调用。)注入Spring的控制器应该始终具有prototype作用域,因为您永远不会希望不同的FXML加载html" target="_blank">元素共享同一个控制器。

我认为这样做的方法是使用模型的prototype作用域将模型实例注入到嵌套控制器中(因此每个控制器都获得自己的模型),然后从顶层控制器配置这些模型。这仍然涉及到一些“手工”布线(您不能使用Spring将您想要的值直接注入到模型中),但它感觉比上面概述的方法更干净一些。当然,不确定它是否真的适用于您的实际用例。

所以:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="model" class="application.Model" scope="prototype" />

    <bean id="nestedController" class="application.NestedController" scope="prototype" autowire="byName"/>

    <bean id="topController" class="application.TopLevelController" scope="prototype" />

</beans>
package application;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Model {
    private final StringProperty text = new SimpleStringProperty();

    public final StringProperty textProperty() {
        return this.text;
    }

    public final String getText() {
        return this.textProperty().get();
    }

    public final void setText(final String text) {
        this.textProperty().set(text);
    }

}
package application;

import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class NestedController {

    @FXML
    private Label label ;

    private Model model ;

    public Model getModel() {
        return model ;
    }

    public void setModel(Model model) {
        this.model = model ;
    }

    public void initialize() {
        label.textProperty().bindBidirectional(model.textProperty());
    }
}
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tab?>

<BorderPane  xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.NestedController">
    <center>
        <Label fx:id="label"/>
    </center>
</BorderPane>

toplevel.fxml:

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

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.Tab?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="application.TopLevelController">
    <center>
        <TabPane>
            <tabs>
                <Tab text="First">
                    <content>
                        <fx:include fx:id="firstTab" source="Nested.fxml" />
                    </content>
                </Tab>
                <Tab>
                    <content>
                        <fx:include fx:id="secondTab" source="Nested.fxml" />
                    </content>
                </Tab>
            </tabs>
        </TabPane>
    </center>
</BorderPane>

TopleVelController必须将值连接到模型中,这有点麻烦。我不确定这是否比用手给整个模型布线更干净...

package application;

import javafx.fxml.FXML;

public class TopLevelController {

    @FXML
    private NestedController firstTabController ;

    @FXML
    private NestedController secondTabController ;

    public void initialize() {
        firstTabController.getModel().setText("First Tab");
        secondTabController.getModel().setText("Second Tab");
    }
}

应用程序程序集相当简单:

package application;

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

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class NestedControllersSpring extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        try (ClassPathXmlApplicationContext context 
                = new ClassPathXmlApplicationContext("application-context.xml")) {

            FXMLLoader loader = new FXMLLoader(getClass().getResource("TopLevel.fxml"));
            loader.setControllerFactory(cls -> context.getBean(cls));

            primaryStage.setScene(new Scene(loader.load(), 600, 400));
            primaryStage.show();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
 类似资料:
  • Java FXML文档要求创建两个变量,如下所示: 什么决定控制器变量名?是否总是只有include id后跟“controller”?

  • 问题内容: 我正在尝试使用SceneBuilder用JavaFX构建一个简单的GUI,在这里我使用MenuItem(在中)选择一个根文件夹。然后,该文件夹的内容会在TextArea中列出,然后再次包装在TabPane中(,是嵌套的FXML,其中包括)。 我以这篇文章为起点来习惯MVC。不幸的是,由于我没有明确调用它,所以我不知道如何使嵌套的FXML侦听或绑定到外部FXML。现在,我只是停留在标签中

  • 我一次又一次地寻找这件事,但毫无结果。我有一个连接到控制器的JavaFXFXML窗口;此窗口已打开。单击窗口上的按钮会触发打开另一个FXML文件,该文件链接到其各自的控制器。 第二个窗口(optionsUI.fxml和optioncontroller)有几个单选按钮。单击一个按钮时,我希望在mainUI窗口中更改图像/按钮的位置。我该怎么做呢? 主控制器: 我面临的主要问题是在单选按钮上添加一个事

  • 我使用Spring Boot2.0.2和Freemarker作为我的web应用程序。我的webapp的静态内容无法加载嵌套/前缀控制器。 spring Boot:将REST与静态内容分离 如有任何帮助,我们将不胜感激。

  • 问题内容: 我试图在JavaFX中获得TableView来动态显示内容。 当我运行程序时,出现此错误: 我的控制器名为“ UserInterfaceController.java”,它与FXML文件位于同一软件包下,我也已将该软件包导入了FXML中。为什么找不到控制器? FXML文件: 控制器: 主类: 问题答案: 使用FXML的全限定名来引用您的控制器: 后续问题的答案 对以下问题的回答解释了为

  • 我试图在JavaFX中获得一个TableView来动态显示内容。 当我运行程序时,出现以下错误: 我的控制器名为“UserInterfaceController.java”,它与FXML文件在同一个包下,我也在FXML中导入了包。为什么找不到控制器? FXML文件: 控制器: 主要类: