依赖注入和JavaFX [英] Dependency Injection and JavaFX

查看:933
本文介绍了依赖注入和JavaFX的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于JavaFX运行时需要实例化我的Application对象和我的所有控制器对象,所以如何将这些对象注入这些对象?



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



谢谢!

解决方案

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



所以如果你希望Spring为你创建控制器实例,可以这样简单:

  ApplicationContext context = ... ; 

FXMLLoader loader = new FXMLLoader(getClass()。getResource(path / to / fxml));
loader.setControllerFactory(context :: getBean);
父根= loader.load();
SomeController controller = loader.getController(); //如果你需要它...
// ...

现在 FXMLLoader 将为类<?>创建控制器实例c 通过调用 context.getBean(c);



您可以有一个配置:

  @Configuration 
public class AppConfig {

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

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

// ...
}

with

  public class SomeController {

//由FXMLLoader注入:
@ FXML
private TextField someTextField;

//注入Spring:
@Inject
private MyService service;

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

//事件处理程序:
@FXML
private void performAction(ActionEvent e){
service.doAction(...);
}
}

如果您没有使用DI框架,你想用手做注射,你可以这么做,但它涉及到相当多的反思。以下显示了如何(并将给您一个了解Spring为您做了多少丑陋工作的想法!):

  FXMLLoader加载器= new FXMLLoader(getClass()。getResource(path / to / fxml)); 
MyService service = new MyServiceImpl();
loader.setControllerFactory((Class<?> type - > {
try {
//寻找构造函数以MyService作为参数
for(Constructor<?> c:type.getConstructors()){
if(c.getParameterCount()== 1){
if(c.getParameterTypes()[0] == MyService.class){
return c.newInstance(service);
}
}
}
//没有找到合适的构造函数,只使用默认构造函数:
return type.newInstance( );
} catch(异常exc){
throw new RuntimeException(exc);
}
});
父根= loader.load();
// ...

然后只需执行

  public class SomeController {

private final MyService service;

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

//由FXMLLoader注入:
@FXML
private TextField someTextField;

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

//事件处理程序:
@FXML
private void performAction(ActionEvent e){
service.doAction(...);
}
}

最后,您可能想查看 afterburner.fx ,这是一个非常轻量级(最好的方式)JavaFX特定的DI框架。 (它使用惯例超配置方法,您只需将FXML文件名称与控制器类名称相匹配,并可选择使用CSS文件名,并且一切正常。)


Since JavaFX runtime wants to instantiate my Application object and all of my controller objects, how do I inject dependencies into these objects?

If objects were instantiated by a DI framework, like Spring, the framework would wire up all the dependencies. If I was instantiating the objects manually, I would provide the dependencies through constructor parameters. But what do I do in a JavaFX application?

Thanks!

解决方案

You can specify a controller factory for the FXMLLoader. The controller factory is a function that maps the controller class to an object (presumably, but not necessarily, an instance of that class) which will be used as the controller.

So if you want Spring to create the controller instances for you, this can be as simple as:

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...
// ...

And now the FXMLLoader will create controller instances for a Class<?> c by calling context.getBean(c);.

So, e.g., you could have a configuration:

@Configuration
public class AppConfig {

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

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

    // ...
}

with

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(...);
    }
}

If you're not using a DI framework, and you want to do the injection "by hand", you can do so, but it involves using quite a lot of reflection. The following shows how (and will give you an idea of how much ugly work Spring is doing for you!):

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();
// ...

and then just do

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(...);
    }
}

Finally, you might want to check out afterburner.fx, which is a very lightweight (in all the best ways) JavaFX-specific DI framework. (It uses a convention-over-configuration approach, where you just match FXML file names to controller class names, and optionally CSS file names, and everything just works.)

这篇关于依赖注入和JavaFX的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆