JavaFX和Spring - bean不会自动装配 [英] JavaFX and Spring - beans doesn't Autowire

查看:897
本文介绍了JavaFX和Spring - bean不会自动装配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在组合JavaFX和Spring方面遇到了问题。我有简单的JavaFX应用程序,它工作正常。现在我想尝试添加一些Spring。我按照 JavaFX 2 with Spring Tutorial 进行了操作。我的代码:

I have problems with combining JavaFX and Spring. I have simple JavaFX application, which works fine. Now I am trying to add some Spring to it. I followed JavaFX 2 with Spring Tutorial. My code:

src/main
|
|_java/mycompany/imageviewer
|   |
|   |_Startup.java
|   |_controller/ImageViewController.java
|   |_dataprovider
|       |impl/DataProviderImpl.java
|   |_config
|       |_SpringFxmlLoader.java
|       |_SpringApplicationConfig.java
|_resources/mycompany/view/ImageViewer.fxml

Startup.java 是带有main的文件:

Startup.java is file with main:

public class Startup extends Application {

    private static final SpringFxmlLoader loader = new SpringFxmlLoader();

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        ...    
        Parent root = (Parent) loader.load("/mycompany/imageviewer/view/ImageViewer.fxml","mycompany/imageviewer/bundle/bundle");
        Scene scene = new Scene(root);
        ...css etc...
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

ImageviewerController.java

@Controller
public class ImageViewerController {
    private static final Logger LOG = Logger.getLogger(ImageViewerController.class);
    @FXML
    ...    
    @Autowired
    private DataProvider dataProvider;

    public ImageViewerController() {
        LOG.debug("Controller initialized. DataProvider is null: "+(dataProvider==null));
    }

DataProviderImpl.java

@Service("dataProvider")
public class DataProviderImpl implements DataProvider {
    private static final Logger LOG = Logger.getLogger(DataProviderImpl.class);

    public DataProviderImpl() {
        LOG.debug("DataProviderImpl initialized.");
    }
    ...methods...
}

我的SpringFxmlLoader在教程中看起来与此类似:

My SpringFxmlLoader looks similar to this in tutorial:

public class SpringFxmlLoader {

    private static final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringApplicationConfig.class);

    public Object load(String url, String resources) {
        FXMLLoader loader = new FXMLLoader();
        loader.setControllerFactory(clazz -> applicationContext.getBean(clazz));
        try {
            return loader.load(getClass().getResource(url), ResourceBundle.getBundle(resources));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

我的SpringApplicationConfig:

My SpringApplicationConfig:

@Configuration
@ComponentScan(basePackages = {"mycompany.imageviewer.controller", "mycompany.imageviewer.dataprovider.impl" })
public class SpringApplicationConfig {
    private static final Logger LOG = Logger.getLogger(SpringApplicationConfig.class);

    @Bean
    public DataProvider dataProvider() {
        LOG.debug("Initializing dataProvider via SpringApplicationConfig");
        return new DataProviderImpl();
    }

    @Bean
    public ImageViewerController imageViewerController() {
        LOG.debug("Initializing ImageViewerController via SpringApplicationConfig");
        return new ImageViewerController();
    }
}

在我的应用中,我有带有绑定控制器的ImageViewer.fxml

<AnchorPane fx:controller="mycompany.imageviewer.controller.ImageViewerController" xmlns="http://javafx.com/javafx/8.0.51" xmlns:fx="http://javafx.com/fxml/1" >

当我运行程序时,我得到日志:

When I run program I get logs:

DEBUG [main] mycompany.imageviewer.controller.ImageViewerController:74 - Controller initialized. DataProviderImpl is null: true
DEBUG [main] mycompany.imageviewer.dataprovider.impl.DataProviderImpl:22 - DataProviderImpl initialized.
DEBUG [JavaFX Application Thread] mycompany.imageviewer.controller.ImageViewerController:74 - Controller initialized. DataProviderImpl is null: true

这表明我的控制器初始化了两次,并且 dataProvider 未正确绑定。令我困惑的是,当我意外地在 ComponentScan 中以错误的包装错误地写错了 basePackages

Which shows, that my controller is initialized twice, and the dataProvider is not properly binded. What made me confused, was that when I accidentaly wrote wrong basePackages in ComponentScan in this way with wrong packages:

@ComponentScan(basePackages = {"mycompany.imageviewer.dataprovider.controller", "mycompany.imageviewer.dataprovider.dataprovider.impl" })

Beans初始化中的方法SpringApplicationConfig.java 运行,我从他们那里得到日志:

Beans initialize methods in SpringApplicationConfig.java run and I get logs from them:

2015-09-06 16:52:29,420 DEBUG [main] com.capgemini.starterkit.imageviewer.config.SpringApplicationConfig:19 - Initializing dataProvider via SpringApplicationConfig
2015-09-06 16:52:29,431 DEBUG [main] com.capgemini.starterkit.imageviewer.dataprovider.impl.DataProviderImpl:22 - DataProviderImpl initialized.
DEBUG [main] mycompany.imageviewer.config.SpringApplicationConfig:25 - Initializing ImageViewerController via SpringApplicationConfig
DEBUG [main] mycompany.imageviewer.controller.ImageViewerController:74 - Controller initialized. DataProviderImpl is null: true
DEBUG [JavaFX Application Thread] mycompany.imageviewer.controller.ImageViewerController:74 - Controller initialized. DataProviderImpl is null: true

当我运行 basePackages =com.capgemini时。 starterkit.imageviewer效果与第一种情况相同。
我是春天的新手,可能我犯了一些简单的错误,但是我找不到它们,所以如果有人能帮我配置那个很棒的弹簧。: - )

When I run basePackages = "com.capgemini.starterkit.imageviewer" effect is the same like in the first case. I am new to spring and probably I made some simple mistakes, but I am not able to find them, so if anybody can help me configure spring that would be great.:-)

推荐答案

FXMLLoader.load(URL,ResourceBundle) 方法是一个静态方法 - 所以它实际上不关注你实例化的 FXMLLoader 实例,因此忽略 controllerFactory 引用你的Spring bean工厂。

The FXMLLoader.load(URL, ResourceBundle) method you are calling is a static method - so it actually pays no attention to the FXMLLoader instance you instantiated, and consequently ignores the controllerFactory which references your Spring bean factory.

重写你的 SpringFXMLLoader 类如下所示:

public class SpringFxmlLoader {

    private static final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringApplicationConfig.class);

    public Object load(String url, String resources) {
        FXMLLoader loader = new FXMLLoader();
        loader.setControllerFactory(clazz -> applicationContext.getBean(clazz));
        loader.setLocation(getClass().getResource(url));
        loader.setResources(ResourceBundle.getBundle(resources));
        try {
            return loader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

这使用实例方法 loader。 load() 将使用你的控制器工厂:即它将使用Spring来实例化控制器。

This uses the instance method loader.load() which will use your controller factory: i.e. it will use Spring to instantiate the controller.

你看到的原因加载两次的控制器默认情况下bean工厂给出控制器单例作用域并使其热切创建,因此只要你创建bean工厂( applicationContext ),它就会创建一个控制器。该控制器将初始化 dataProvider (当然,构造函数完成后只有)。然后调用静态 FXMLLoader.load(...)方法通过通常的机制创建第二个控制器(即通过调用它的无参数构造函数)。那个实例不会随时初始化 dataProvider

The reason you see the controller loaded twice is that by default the bean factory gives the controller singleton scope and makes it eagerly created, so as soon as you create the bean factory (applicationContext) it creates a controller. That controller will have its dataProvider initialized (but only after the constructor has completed, of course). Then the call to the static FXMLLoader.load(...) method creates a second controller by the usual mechanism (i.e. by calling its no-arg constructor). That instance will not have its dataProvider initialized at any time.

顺便说一下,你可能不想要控制器是单身人士。如果你要加载你的FXML文件两次,得到两个 Parent 的实例,你可能需要每个实例都有自己的控制器,否则会出现奇怪的行为。我建议将控制器作为原型(这意味着bean工厂将在每次请求时创建一个新实例,而不是重用单个实例)。您可以在配置类中执行以下操作:

As an aside, you probably don't want controllers to be singletons. If you were to load your FXML file twice, to get two instances of the Parent, you would likely need each instance to have its own controller, otherwise strange behavior would ensue. I would recommend making the controller a prototype (which means the bean factory will create a new instance every time one is requested, instead of reusing a single instance). You can do this with the following in your config class:

@Configuration
@ComponentScan(basePackages = {"mycompany.imageviewer.controller", "mycompany.imageviewer.dataprovider.impl" })
public class SpringApplicationConfig {
    private static final Logger LOG = Logger.getLogger(SpringApplicationConfig.class);

    @Bean
    public DataProvider dataProvider() {
        LOG.debug("Initializing dataProvider via SpringApplicationConfig");
        return new DataProviderImpl();
    }

    @Bean
    @Scope("prototype")
    public ImageViewerController imageViewerController() {
        LOG.debug("Initializing ImageViewerController via SpringApplicationConfig");
        return new ImageViewerController();
    }
}

这篇关于JavaFX和Spring - bean不会自动装配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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