繁体   English   中英

JavaFX和Spring - bean不会自动装配

[英]JavaFX and Spring - beans doesn't Autowire

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

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的文件:

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在教程中看起来与此类似:

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:

@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" >

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

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

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

Bean在SpringApplicationConfig.java运行中初始化方法,我从中获取日志:

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"效果与第一种情况相同。 我是春天的新手,可能我犯了一些简单的错误,但是我找不到它们,所以如果有人能帮我配置那个很棒的弹簧。:-)

您调用的FXMLLoader.load(URL, ResourceBundle)方法是一个static方法 - 因此它实际上不关注您实例化的FXMLLoader实例,因此忽略了引用您的Spring bean工厂的controllerFactory

重写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来实例化控制器。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM