gpt4 book ai didi

java - Spring-注入(inject)的变量为空

转载 作者:行者123 更新时间:2023-11-30 10:46:56 24 4
gpt4 key购买 nike

我正在尝试将 Spring DI 与 JavaFx 一起使用 我有一个 MainController 类,它作为 Bean 加载到 AppConfig 中,然后是另一个将使用 MainService 的 MenuController 类。但是注入(inject)的服务在调用时为空。

问题

  • 可能是什么原因,它没有注入(inject)变量?
  • 在 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();
}
}
}
}

菜单 Controller 类:

这是我需要访问 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) -> {
...
});
}
}

最佳答案

问题在于事情发生的顺序。

从 Spring 的角度来看,当您请求 bean 时,它是通过调用 menuController() 创建的。应用程序配置中的方法,然后是 @Inject - 带注释的字段被初始化(通过反射),然后它返回 bean。

然而,menuController()方法通过加载 fxml 文件创建 Controller ,然后从 FXMLLoader 检索 Controller . initialize() Controller 中的方法由 FXMLLoader 调用作为 load() 的一部分过程。显然,这发生在你的 menuController() 之前返回(因为它发生在 loader.load() 返回之前);所以initialize()在 Spring 有机会初始化注入(inject)的字段之前调用。

最快的解决方法可能是为服务定义一个 setter 方法,并在服务初始化时简单地调用服务上的方法:

public class MenuController extends AbstractController implements Initializable {
// ...

private MainService mainService;

@Inject
public void setMainService(MainService mainService) {
this.mainService = mainService ;
mainService.currentProgramProperty().addListener((observable, oldValue, newValue) -> {
// ...
});
}

@Override
public void initialize(URL location, ResourceBundle resources) {

disableMenuElements();
}
}

当我使用 Spring 管理我的 JavaFX 应用程序时,我倾向于使用完全不同的方法。我没有让 Controller 访问 View ,然后从 Controller 检索 View ,而是告诉 FXMLLoader使用 Spring 实例化 Controller ,通过 controllerFactory .然后当你调用load()FXMLLoader 上,它从 Spring 请求 Controller 作为 bean,因此 FXMLLoader接收一个注入(inject)了所有依赖项的 bean。然后当它调用 initialize() 时在 Controller 上,依赖项已经存在。

所以

@Configuration
public class AppConfiguration {

@Bean
// ??? surely a service should be singleton, not prototype, scope...
@Scope("prototype")
public MainService mainService() {
return new InMemoryMainService();
}

@Bean
@Scope("prototype")
public MainController mainController() throws IOException {
return new MainController();
}

@Bean
@Scope("prototype")
public MenuController menuController() throws IOException {
return new MenuController();
}


}

并且您的菜单 Controller 与您拥有的一样:

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

现在你可以做

public class App extends Application {

private static final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfiguration.class);

@Override
public void start(Stage primaryStage) throws Exception {
// should fix name, you should not start your own pacakge names "java"
FXMLLoader loader = new FXMLLoader(getClass().getResource("/java/com/akos/fxml/Main.fxml"));
loader.setControllerFactory(context::getBean);
Scene scene = new Scene(loader.load());
primaryStage.setScene(scene);
primaryStage.show();
}

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

请注意,您可以通过调用

在应用程序代码中获取对 Controller 的引用
MainController mainController = loader.getController();

您调用了loader.load()之后.这为您提供了对 Controller 的引用 FXMLLoader创建;即 Spring 创建的那个(因为 Controller 工厂指示 FXMLLoader 使用 Spring)。 (不过,在我看来,你真的不需要引用 Controller ; Controller 专门知道如何在 View 和模型(服务)之间进行通信;如果你想从外部更改 UI,你应该更新模型这样做,然后 Controller 将观察模型的变化并更新 View 。)

我不完全清楚“递归连接”应该做什么。如果您通过 <fx:include> 加载菜单在主 fxml 文件中, Controller 工厂将传播到包含的 fxml 文件,因此 MenuController也将从 spring 上下文实例化,并根据需要注入(inject)服务。如果你在其他地方加载它,你只需要在加载它时设置 Controller 工厂,如上面的主 fxml 文件所示。所有这些都假设您的 Controller 在 fxml 文件中指定为 <fx:controller> ,我认为您的其他代码一定是这种情况。

关于java - Spring-注入(inject)的变量为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36317608/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com