JavaFX和Spring - bean不会自动装配 [英] JavaFX and Spring - beans doesn't Autowire
问题描述
我在组合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屋!