JavaFX。在哪里放置服务类引用? Controller 或主应用程序入门类(class)?

标签 java javafx fxml

我使用 JavaFX 应用程序并使用 FXML 来实现 MVC 模式。我已经进行了概念验证,现在开始创建 JavaFX 用户界面。

根据我之前使用 Spring MVC 的经验,通常会创建服务并通过注释将它们注入(inject)到 Controller 类中。但对于 JavaFX,我找不到任何建议如何做到这一点。另外,我不确定是否必须将服务放入 Controller ,或从 Controller 调用主类方法。第二种解决方案在主应用程序类中保存服务引用。

请注意我的应用程序在并发线程中运行服务类。所以它们都实现了Runnable接口(interface)

最佳答案

我会避免 Controller 需要引用主应用程序类,因为它引入了不必要的额外依赖项。因此,让每个 Controller 保留对服务对象的引用。

要向 Controller 提供服务,您基本上可以使用 this question 中概述的技术之一.

基本上有三种方法可以做到这一点:

创建 Controller 并将其设置在 FXMLLoader 中直接

在此版本中,您使用 fx:controller FXML 文件根元素中的属性(这样做会导致抛出异常)。

给定

public interface Service { ... }

public class SomeController {

    private final Service service ;

    public SomeController(Service service) {
        this.service = service ;
    }

    // ...
}

然后您可以使用

加载 FXML 文件
Service service = ... ;
FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml/file.fxml"));
SomeController controller = new SomeController(Service.class);
loader.setController(controller);
Parent uiRoot = loader.load();

FXMLLoader 检索 Controller 并设置服务

如果您希望能够使用 fx:controller属性,您的 Controller 类必须有一个无参构造函数。此时可以在 Controller 上设置FXMLLoader后的服务。已完成加载。这看起来像:

public class SomeController {

    private Service service ;

    public void initService(Service service) {
        this.service = service ;
        // update UI with values from service...
    }

    // ...
}

请注意,这里您可能需要重构 initialize() 中的一些代码。方法,因为该代码可能取决于服务,当 initialize() 时不会设置该服务被调用。只需将任何此类代码移至 initService(...)方法。现在加载 FXML 文件看起来像

Service service = ... ;
FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml/file.fxml"));
Parent uiRoot = loader.load();
SomeController controller = loader.getController();
controller.initService(service);

使用 Controller 工厂

第三种方法使用 Controller 工厂。这稍微复杂一些,但有一些优点。特别是,如果您的 fxml 文件使用 fx:include标签,当加载包含的 fxml 文件时, Controller 工厂将被重用,因此这些 Controller 也可以初始化服务对象。使用上述两种方法管理包含的 fxml 文件是可能的,但有点复杂。

Controller 工厂本质上是一个映射 Class<?> 的函数。到应该使用的 Controller (大概是该类之一,尽管有这样的要求)。默认 Controller 工厂仅调用 newInstance()关于Class<?>对象(这就是为什么你需要一个无参数构造函数)。这是一个通用 Controller 工厂实现,它调用一个采用 Service 的构造函数。如果存在参数,如果不存在则调用无参数构造函数。

Service service = ... ;

Callback<Class<?>, Object> controllerFactory = type -> {
    try {
        for (Constructor<?> c : type.getConstructors()) {
            if (c.getParameterCount() == 1 
                && c.getParameterTypes()[0] == Service.class) {

                return c.newInstance(service);
            }
        }
        return type.newInstance();
    } catch (Exception e) {
        throw new RuntimeException(e);
   }
};

您可以创建一次并将其用于您加载的任何 FXML(请注意,它引用单个 Service 实例):

FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml/file.fxml"));
loader.setControllerFactory(controllerFactory);
Parent uiRoot = loader.load();

这将与fx:controller一起使用属性,即使它引用一个类,该类的构造函数采用 Service参数(例如上面的第一个 Controller 示例)。

如果您习惯依赖注入(inject)框架,您可能会对afterburner.fx感兴趣。亚当·比恩 (Adam Bien)这是通过设置一个 Controller 工厂来检查 @Inject 的 Controller 类来实现的。注释并在 Controller 上设置这些值,因此您所要做的就是在 Controller 中注释服务字段并遵循特定的 afterburner.fx 命名约定,一切都会自动发生。

我还推荐this article ,同样由 Adam Bien 撰写,其中讨论了与 Controller 中的服务进行通信的一些策略(包括处理并发问题)。

关于JavaFX。在哪里放置服务类引用? Controller 或主应用程序入门类(class)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30818052/

相关文章:

java - 我正在 Java 中序列化一个对象,但在反序列化时,它的状态与预期不符。如何保留对象的状态?

java - Java 中的 TCP 传输非常慢

java - fxml - 从属性读取值

java - jfx中的setHeight()设置高度用什么单位

java - 一个空的父类(super class)?

java - 将项目集合提供给 java FXML 中的自定义扩展组合框

java - 如何平滑地执行放大和缩小以及在 TEXTVIEW 上的特定缩放位置进行缩放?

java - 在字节码层面,Java的Class.getEnumConstants()如何知道哪些类是枚举类?

JavaFx 程序抛出 java.lang.reflect.InitationTargetException

multithreading - JavaFX UI 通过 Platform.runLater() 使用多个线程卡住