java Spring注入变量为null
我正在尝试将SpringDI与JavaFx结合使用,我有一个MainController类,它作为Bean加载到AppConfig中,然后是另一个MenuController类,它将使用MainService。但注入的服务在调用时为null
问题
- 它没有注入变量的原因是什么李>
- 在setControllerFactory方法的一个示例中,它们返回了appContext。getBean(clazz),但我不知道如何访问上下文。我需要如何设置工厂,以及如何设置工厂李>
- 我需要递归地连接bean吗李>
我的代码
应用程序类:
public class App extends Application {
private static final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfiguration.class);
@Override
public void start(Stage primaryStage) throws Exception {
MainController mainController = context.getBean(MainController.class);
Scene scene = new Scene(mainController.getView());
primaryStage.setScene(scene);
recursiveWire(context, mainController.getView());
primaryStage.show();
}
public void recursiveWire(AnnotationConfigApplicationContext context, Object root) throws Exception {
context.getAutowireCapableBeanFactory().autowireBean(root);
context.getAutowireCapableBeanFactory().initializeBean(root, null);
for (Field field : root.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(FXML.class) && !Node.class.isAssignableFrom(field.getType())) {
recursiveWire(context, field.get(root));
}
}
}
public static void main(String[] args) {
launch(args);
}
}
AppConfiguration类:
在这里,我尝试设置ControllerFactory,正如我看到一些建议一样,但它并没有改变结果
@Configuration
public class AppConfiguration {
@Bean
@Scope("prototype")
public MainService mainService() {
return new InMemoryMainService();
}
@Bean
@Scope("prototype")
@DependsOn("mainService")
public MainController mainController() throws IOException {
return (MainController) loadController("/java/com/akos/fxml/Main.fxml");
}
@Bean
@Scope("prototype")
public MenuController menuController() throws IOException {
return (MenuController) loadController("/java/com/akos/fxml/Menu.fxml");
}
protected Object loadController(String url) throws IOException {
InputStream fxmlStream = null;
try {
fxmlStream = getClass().getResourceAsStream(url);
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource(url));
Node view = loader.load(fxmlStream);
AbstractController controller = loader.getController();
loader.setControllerFactory(clazz -> controller);
controller.setView(view);
return controller;
} finally {
if (fxmlStream != null) {
fxmlStream.close();
}
}
}
}
菜单控制器类:
这里是我需要访问mainService的地方,但它是空的
public class MenuController extends AbstractController implements Initializable {
...
@Inject
MainService mainService;
@Override
public void initialize(URL location, ResourceBundle resources) {
disableMenuElements();
mainService.currentProgramProperty().addListener((observable, oldValue, newValue) -> {
...
});
}
}
# 1 楼答案
问题在于事情发生的顺序
从Spring的角度来看,当您请求bean时,它是通过调用应用程序配置中的
menuController()
方法创建的,然后初始化@Inject
注释的字段(通过反射),然后返回bean但是,
menuController()
方法通过加载fxml文件,然后从FXMLLoader
中检索控制器来创建控制器。控制器中的initialize()
方法由FXMLLoader
调用,作为load()
进程的一部分。显然,这发生在menuController()
返回之前(因为它发生在loader.load()
返回之前);因此initialize()
在Spring有机会初始化注入的字段之前被调用最快的修复方法可能是为服务定义setter方法,并在初始化服务时简单地调用服务上的方法:
在使用Spring管理JavaFX应用程序时,我倾向于使用完全不同的方法。我不是让控制器访问视图,然后从控制器检索视图,而是告诉
FXMLLoader
使用Spring通过controllerFactory
实例化控制器。然后,当您在FXMLLoader
上调用load()
时,它从Spring以bean的形式请求控制器,因此FXMLLoader
接收到一个注入了其所有依赖项的bean。然后,当它在控制器上调用initialize()
时,依赖项已经存在所以
你的菜单控制器和你的一样:
现在你可以做了
请注意,您可以通过调用
在之后,您已经调用了
loader.load()
。这提供了对FXMLLoader
创建的控制器的引用;i、 e.Spring创建的一个(因为控制器工厂指示FXMLLoader
使用Spring)。(不过,在我看来,您真的不需要对控制器的引用;控制器特别知道如何在视图和模型(服务)之间进行通信);如果要从外部更改UI,则应更新模型以进行更改,然后控制器将观察模型中的更改并更新视图。)我不完全清楚“递归连接”应该做什么。如果通过主fxml文件中的
<fx:include>
加载菜单,控制器工厂将传播到包含的fxml文件,因此MenuController
也将从spring上下文中实例化,并根据需要注入服务。如果在其他地方加载它,只需在加载时设置控制器工厂,如上面的主fxml文件所示。所有这些都假设您的控制器是在fxml文件中用<fx:controller>
指定的,我认为您的其他代码中肯定是这样