dependency injection和JavaFX

由于JavaFX运行时想要实例化我的应用程序对象和我的所有控制器对象,我如何注入依赖关系到这些对象?

如果对象是由DI框架实例化的,就像Spring一样,框架将连接所有的依赖关系。 如果我手动实例化对象,我会通过构造函数参数提供依赖关系。 但是,我在JavaFX应用程序中做什么?

谢谢!

您可以为FXMLLoader指定一个控制器工厂 。 控制器工厂是一个将控制器类映射到将用作控制器的对象(可能是,但不一定是该类的一个实例)的函数。

所以如果你想让Spring为你创build控制器实例,可以这样简单:

 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创build控制器实例context.getBean(c);

所以,例如,你可以有一个configuration:

 @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框架,并且想“手动”注入,你可以这样做,但是它涉及到很多reflection。 下面的内容展示了如何(并且会给你一个关于Spring为你做了多less难看的工作的想法!):

 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框架。 (它使用了一种常规的configuration方法,只要将FXML文件名与控制器类的名称以及可选的CSS文件名进行匹配,一切正常。)