简体   繁体   English

如何在 JavaFX 控制器中使用 Guice?

[英]How can i use Guice in JavaFX controllers?

I have a JavaFX application where I would like to introduce Guice because my Code is full of factorys right now, only for the purpose of testing.我有一个 JavaFX 应用程序,我想在其中引入 Guice,因为我的代码现在充满了工厂,仅用于测试目的。

I have one use case where i have a controller class of a certain view.我有一个用例,我有一个特定视图的控制器类。 This controller class has a viewmodel and I pass the model to the viewmodel via the constructor of the controller class.这个控制器类有一个视图模型,我通过控制器类的构造函数将模型传递给视图模型。

In the controller class I have a contactservice object that provides the edit/save/delete operations.在控制器类中,我有一个 contactservice 对象,它提供编辑/保存/删除操作。 As of now I have an interface of that object and provide an implementation and a Mock.到目前为止,我有一个该对象的接口,并提供了一个实现和一个 Mock。 This object can be retrieved by a Factory.getInstance() method.可以通过 Factory.getInstance() 方法检索此对象。

What I want to do is something like this:我想做的是这样的:

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---------------------------------------------------
    }
}

How can I achive this?我怎样才能做到这一点? Is it a good a idea to do something like that?做这样的事情是个好主意吗? While I was searching for a solution I found fx-guice and similar frameworks but how can i combine these two?当我在寻找解决方案时,我发现了fx-guice和类似的框架,但我如何将这两者结合起来? Specially how can I let this fields be injected AND instanciate the controller myself or at least give it some constructor args?特别是我怎样才能让这个字段被注入并自己实例化控制器,或者至少给它一些构造函数参数?

I don't use Guice, but the simplest approach would appear to be just to use a controller factory on the FXMLLoader .我不使用 Guice,但最简单的方法似乎只是在FXMLLoader上使用控制器工厂。 You can create a controller factory that instructs the FXMLLoader to use Guice to initialize your controllers:您可以创建一个控制器工厂,指示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();

There's a good DI framework for javaFX called afterburner.fx . javaFX 有一个很好的 DI 框架,称为afterburner.fx Check it out, I think it's the tool you're looking for.检查一下,我认为这是您正在寻找的工具。

Assuming you (could) instantiate the controller by hand/guice and not from the FXML, you could use https://github.com/google/guice/wiki/AssistedInject if you need to pass any non DIable parameter to the constructor.假设您(可以)手动/guice 而不是从 FXML 实例化控制器,如果您需要将任何非 DIable 参数传递给构造函数,则可以使用https://github.com/google/guice/wiki/AssistedInject You would then set the controller manually to the FXMLLoader with .setController(this) and load the FXML file in the constructor of the controller.然后,您可以使用 .setController(this) 将控制器手动设置为 FXMLLoader,并在控制器的构造函数中加载 FXML 文件。

Not sure if there are any drawbacks, but this kind of system seems to work for me :)不确定是否有任何缺点,但这种系统似乎对我有用:)

To use JavaFx with Guice :JavaFxGuice结合使用:

  1. Extend javafx.application.Application & call launch method on that class from the main method.扩展 javafx.application.Application 并从 main 方法调用该类的启动方法。 This is the application's entry point.这是应用程序的入口点。
  2. Instantiate dependency injection container of your choice.实例化您选择的依赖注入容器。 Eg Google Guice or Weld.例如 Google Guice 或 Weld。
  3. In application's start method, instantiate FXMLLoader and set it's controller factory to obtain controllers from the container.在应用程序的 start 方法中,实例化 FXMLLoader 并设置它的控制器工厂以从容器中获取控制器。 Ideally obtain the FXMLLoader from the container itself, using a provider.理想情况下,使用提供程序从容器本身获取 FXMLLoader。 Then give it an .fxml file resource.然后给它一个 .fxml 文件资源。 This will create content of the newly created window.这将创建新创建窗口的内容。
  4. Give the Parent object instantiated in previous step to Stage object (usually called primaryStage) supplies as an argument to the start(Stage primaryStage) method.将上一步中实例化的 Parent 对象提供给 Stage 对象(通常称为 primaryStage)作为 start(Stage primaryStage) 方法的参数。
  5. Display the primaryStage by calling it's show() method.通过调用它的 show() 方法来显示 primaryStage。

Code Example MyApp.java代码示例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();
    }

}

Then FXMLLoaderProvider.java然后 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;
        }
    
    }

Thanks to mr.感谢先生。 Pavel Pscheidl who provided us with this smart code at Integrating JavaFX 8 with Guice Pavel PscheidlIntegrating JavaFX 8 with Guice 上为我们提供了这个智能代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM