簡體   English   中英

依賴注入和 JavaFX

[英]Dependency Injection and JavaFX

由於 JavaFX 運行時想要實例化我的 Application 對象和我的所有控制器對象,我如何將依賴項注入這些對象?

如果對象由 DI 框架(如 Spring)實例化,則該框架將連接所有依賴項。 如果我手動實例化對象,我將通過構造函數參數提供依賴項。 但是我在 JavaFX 應用程序中做什么?

謝謝!

您可以為FXMLLoader指定控制器工廠 控制器工廠是一個將控制器類映射到將用作控制器的對象(可能,但不一定是該類的實例)的函數。

因此,如果您希望 Spring 為您創建控制器實例,這可以很簡單:

ApplicationContext context = ... ;

FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml"));
loader.setControllerFactory(context::getBean);
Parent root = loader.load();
SomeController controller = loader.getController(); // if you need it...
// ...

現在FXMLLoader將通過調用context.getBean(c);Class<?> c創建控制器實例context.getBean(c); .

所以,例如,你可以有一個配置:

@Configuration
public class AppConfig {

    @Bean
    public MyService service() {
        return new MyServiceImpl();
    }

    @Bean
    @Scope("prototype")
    public SomeController someController() {
        return new SomeController();
    }

    // ...
}

public class SomeController {

    // injected by FXMLLoader:
    @FXML
    private TextField someTextField ;

    // Injected by Spring:
    @Inject
    private MyService service ;

    public void initialize() {
        someTextField.setText(service.getSomeText());
    }

    // event handler:
    @FXML
    private void performAction(ActionEvent e) {
        service.doAction(...);
    }
}

如果您沒有使用 DI 框架,並且您想“手動”進行注入,您可以這樣做,但它涉及使用相當多的反射。 下面展示了如何(並將讓您了解 Spring 為您做了多少丑陋的工作!):

FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml"));
MyService service = new MyServiceImpl();
loader.setControllerFactory((Class<?> type -> {
    try {
        // look for constructor taking MyService as a parameter
        for (Constructor<?> c : type.getConstructors()) {
            if (c.getParameterCount() == 1) {
                if (c.getParameterTypes()[0]==MyService.class) {
                    return c.newInstance(service);
                }
            }
        }
        // didn't find appropriate constructor, just use default constructor:
        return type.newInstance();
    } catch (Exception exc) {
        throw new RuntimeException(exc);
    }
});
Parent root = loader.load();
// ...

然后就做

public class SomeController {

    private final MyService service ;

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

    // injected by FXMLLoader:
    @FXML
    private TextField someTextField ;

    public void initialize() {
        someTextField.setText(service.getSomeText());
    }

    // event handler:
    @FXML
    private void performAction(ActionEvent e) {
        service.doAction(...);
    }
}

最后,您可能想查看afterburner.fx ,這是一個非常輕量級(以所有最佳方式)JavaFX 特定的 DI 框架。 (它使用約定優於配置的方法,您只需將 FXML 文件名與控制器類名以及可選的 CSS 文件名相匹配,一切正常。)

我使用 FXMLLoader 類的 setControllerFactory 方法來設置此序列化程序使用的控制器工廠。

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext();
        ctx.register(AppConfig.class);
        ctx.refresh();
        FXMLLoader loader = new FXMLLoader(getClass().getResource("../view/sample.fxml"));
        loader.setControllerFactory(ctx::getBean);

        Parent root = loader.load();
        Controller controller = loader.getController();
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 925, 400));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);

    }
}

然后使用@component 到控制器類

  @Component
        public class Controller {
            @Autowired
            private ItemController itemController;
            @FXML
            private TextField item;
            @FXML
            private TextField quantity;
        }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM