【问题标题】:How can i use Guice in JavaFX controllers?我如何在 JavaFX 控制器中使用 Guice?
【发布时间】:2014-05-05 11:19:16
【问题描述】:

我有一个 JavaFX 应用程序,我想在其中介绍 Guice,因为我的代码 现在到处都是工厂,只是为了测试。

我有一个用例,我有一个特定视图的控制器类。 这个控制器类有一个视图模型,我通过模型将模型传递给视图模型 控制器类的构造函数。

在控制器类中,我有一个提供编辑/保存/删除操作的联系服务对象。 到目前为止,我拥有该对象的接口并提供了一个实现和一个 Mock。此对象可以通过 Factory.getInstance() 方法检索。

我想做的是这样的:

public class NewContactController implements Initializable {
    // ------------------------------------------------------------------------
    // to inject by guice
    // ------------------------------------------------------------------------
    private ContactService contactService;

    @Inject
    public void setContactService(ContactService srv) {
        this.contactService = srv;
    }
    // edit window
    public NewContactController(Contact c) {
        this.viewModel = new NewContactViewModel(c);
    }
    // new window
    public NewContactController() {
        this.viewModel = new NewContactViewModel();
    }

    @FXML
    void onSave(ActionEvent event) {
        //do work like edit a contcat, 
        contactService.editContact(viewModel.getModelToSave());
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // bind to viewmodel---------------------------------------------------
    }
}

我怎样才能做到这一点?做这样的事情是个好主意吗? 当我在寻找解决方案时,我发现了fx-guice 和类似的框架,但是我如何将这两者结合起来呢? 特别是我怎样才能让这些字段被注入并自己实例化控制器,或者至少给它一些构造函数参数?

【问题讨论】:

    标签: java dependency-injection javafx guice


    【解决方案1】:

    我不使用 Guice,但最简单的方法似乎只是在 FXMLLoader 上使用控制器工厂。您可以创建一个控制器工厂,指示FXMLLoader 使用 Guice 来初始化您的控制器:

    final Injector injector = Guice.createInjector(...);
    FXMLLoader loader = new FXMLLoader(getClass().getResource(...));
    loader.setControllerFactory(new Callback<Class<?>, Object>() {
       @Override
       public Object call(Class<?> type) {
           return injector.getInstance(type);
       }
    });
    // In Java 8, replace the above with 
    // loader.setControllerFactory(injector::getInstance);
    Parent root = loader.<Parent>load();
    

    【讨论】:

    • 好的,所以根据定义我必须让任何框架来实例化控制器?那么我可以将控制器任何参数传递给构造函数吗?无论是加力燃烧室还是 Guice,还是说 Spring?我的意思是,如果我的 Controller 会像在 Roboguice 中那样从框架类继承,那么我需要如何编写代码?
    • 很好的答案,只是想提一下,这可以在 java 8 中作为单行来完成:loader.setControllerFactory(injector::getInstance);
    • 澄清一下,这是否需要为每个控制器类添加一个新的加载器?
    【解决方案2】:

    有一个很好的 javaFX DI 框架,名为afterburner.fx。看看吧,我想这就是你要找的工具。

    【讨论】:

    • afterburner.fx 非常棒,如果您还没有太依赖 Guice。
    • afterburner.fx 的最新版本已有近 4 年的历史(2016 年 4 月)。诚然,Guice 自上次发布以来已经过去了大约 1 年,但它也有更多的社区支持
    • @Evvo 我不能代表这个库的创建者 Adam,但我认为他会欢迎任何外部贡献
    【解决方案3】:

    假设您(可以)通过手动/guice 而不是从 FXML 实例化控制器,如果您需要将任何非 DIable 参数传递给构造函数,则可以使用 https://github.com/google/guice/wiki/AssistedInject。然后,您可以使用 .setController(this) 手动将控制器设置为 FXMLLoader,并在控制器的构造函数中加载 FXML 文件。

    不确定是否有任何缺点,但这种系统似乎对我有用:)

    【讨论】:

      【解决方案4】:

      JavaFxGuice 一起使用:

      1. 从 main 方法扩展 javafx.application.Application 并在该类上调用启动方法。这是应用程序的入口点。
      2. 实例化您选择的依赖注入容器。例如。 Google Guice 或 Weld。
      3. 在应用程序的启动方法中,实例化FXMLLoader并设置它的控制器工厂,以便从容器中获取控制器。理想情况下,使用提供程序从容器本身获取 FXMLLoader。然后给它一个 .fxml 文件资源。这将创建新创建的窗口的内容。
      4. 将上一步中实例化的 Parent 对象提供给 Stage 对象(通常称为 primaryStage)作为 start(Stage primaryStage) 方法的参数。
      5. 通过调用它的 show() 方法来显示 primaryStage。

      代码示例 MyApp.java

      public class MyApp extends Application {
          @Override
          public void start(Stage primaryStage) throws IOException {
              Injector injector = Guice.createInjector(new GuiceModule());
              //The FXMLLoader is instantiated the way Google Guice offers - the FXMLLoader instance
              // is built in a separated Provider<FXMLLoader> called FXMLLoaderProvider.
              FXMLLoader fxmlLoader = injector.getInstance(FXMLLoader.class);
      
              try (InputStream fxmlInputStream = ClassLoader.getSystemResourceAsStream("fxml\\login.fxml")) {
                  Parent parent = fxmlLoader.load(fxmlInputStream);
                  primaryStage.setScene(new Scene(parent));
                  primaryStage.setTitle("Access mini Stock App v1.1");
                  primaryStage.show();
                  primaryStage.setOnCloseRequest(e -> {
                      System.exit(0);
                  });
              } catch (IOException ex) {
                  ex.printStackTrace();
              }
          }
      
          public static void main(String[] args) {
      
              launch();
          }
      
      }
      

      然后是 FXMLLoaderProvider.java

      public class FXMLLoaderProvider implements Provider<FXMLLoader> {
          
              @Inject Injector injector;
          
              @Override
              public FXMLLoader get() {
                  FXMLLoader loader = new FXMLLoader();
                  loader.setControllerFactory(p -> {
                      return injector.getInstance(p);
                  });
                  return loader;
              }
          
          }
      

      感谢先生。 Pavel Pscheidl Integrating JavaFX 8 with Guice

      向我们提供了这个智能代码

      【讨论】:

      • 我担心,您从 pavels 帖子中复制了错误的代码。您需要设置控制器工厂的代码。您上面的示例并没有为 fxml 加载器添加 guice 。控制器。
      • @PeterPaulKiefer 我没有注意到,现在我用自己的工作代码编辑了这个例子,非常感谢。
      猜你喜欢
      • 1970-01-01
      • 2015-06-20
      • 1970-01-01
      • 2018-07-24
      • 1970-01-01
      • 2015-03-24
      • 1970-01-01
      • 2016-07-26
      • 2021-02-19
      相关资源
      最近更新 更多