[英]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.