如何通过 Spring XML 配置文件将 Spring bean 自动装配到 FXML 控制器 class
How to autowire Spring beans into a FXML controller class via a Spring XML config file
我有一个 FXML 控制器,它有一些 Spring Bean 依赖项。我找不到在加载控制器之前及时自动装配它们的方法,因为我使用的是自定义 FXML 加载器:
@Bean
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public UserProfile attachDocController() throws IOException {
return (UserProfile) loadController("/myproject/Forms/userProfile.fxml");
}
FXMLLoader loader = null;
protected Object loadController(String url) throws IOException {
loader = new FXMLLoader(getClass().getResource(url));
loader.load();
return loader.getController();
}
使用这种方法,我只能通过 @Autowired
注解直接注入来自动装配 bean:
public class UserProfile {
@Autowired
MyDependency myDependency;
这让我依赖于 Spring,并且以后会给我留下代码可维护性问题。如何将 Spring XML 文件配置中的依赖项自动装配到 FXML 控制器 class 中?类似于:
<bean id="UserProfile" class="myproject.controllerinjection.UserProfile" scope="prototype">
<aop:scoped-proxy proxy-target-class="true"/>
<property name="myDependency" ref="myDependency" />
</bean>
<bean id="myDependency" class="myproject.controllerinjection.MyDependency" scope="prototype">
<aop:scoped-proxy proxy-target-class="true"/>
</bean>
随着项目变大,考虑到长期项目可维护性,这似乎是一条更好的路线。
更新:
我不太习惯 Lambda 表达式。我研究了一下,但是整合@James_D的建议如下:
protected Object loadBeanController(String url) throws IOException {
loader = new FXMLLoader(getClass().getResource(url));
ApplicationContext ctx = WakiliProject.getCtx();
if (ctx != null) {
System.out.println("Load Bean...............");
loader.setControllerFactory(ctx::getBean);
} else {
System.out.println("No App.ctx...............");
}
return loader.getController();
}
每当我尝试调用 MyDependency
的方法时, 都会给出一个空指针。 MyDependency myDependency
永远不会注入 UserProfile
.
当您调用 FXMLLoader.load()
时,它会加载 FXML 文件。如果根元素中有 fx:controller
属性,它会根据指定的 class 创建一个控制器(并将 fx:id
属性元素注入该控制器实例,等等)。然后加载程序 returns FXML 文件的根目录。控制器本质上链接到 FXML 根。
默认情况下,FXMLLoader
通过反射将控制器class映射到一个实例,调用controllerClass.newInstance()
(这有效地调用了控制器的无参数构造函数class).您可以通过在 FXMLLoader
.
上指定 controllerFactory
来配置它,覆盖默认行为
controllerFactory
是一个将 Class<?>
对象(根据 fx:controller
属性中指定的 class 名称构造)映射到控制器实例的函数。如果您使用 Spring 来管理您的控制器实例,您只需要此函数请求 Spring 应用程序上下文(bean 工厂)为您生成控制器实例。所以你基本上可以做 fxmlLoader.setControllerFactory(applicationContext::getBean);
。使用此设置,只需通过 FXMLLoader
加载 fxml 文件将导致 FXMLLoader
从应用程序上下文请求控制器 class。可以使用 Spring 允许的任何方式配置应用程序上下文。
所以你的配置看起来像
@Configuration
public class Config {
@Bean
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public UserProfile attachDocController() throws IOException {
return new UserProfile();
}
}
当然,您现在可以在配置中注入依赖项class:
@Bean
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public UserProfile attachDocController(MyDependency myDependency) throws IOException {
return new UserProfile(myDependency);
}
@Bean
public MyDependency createDependency() {
return new MyDependencyImpl();
}
然后在你的 UI 工作中你可以做到
FXMLLoader loader = new FXMLLoader(getClass().getResource("/myproject/Forms/userProfile.fxml"));
loader.setControllerFactory(applicationContext::getBean);
Parent root = loader.load();
// since everything can be initialized in the controller by D.I., you
// shouldn't need to access it, but if you do for some reason you can do
UserProfile controller = loader.getController();
其中 applicationContext
是您的 Spring 应用程序上下文。无论应用程序上下文使用 XML 配置、基于注释的配置还是 Java 配置,这都有效。
更新
如果由于某种原因您不能使用 Java 8 或更高版本,则调用 setControllerFactory
与 Java 7 兼容,如下所示:
loader.setControllerFactory(new Callback<Class<?>, Object>() {
@Override
public Object call(Class<?> c) {
return applicationContext.getBean(c);
}
});
您需要 applicationContext
成为字段或 final
局部变量才能在 Java 7 中工作。请注意,在撰写本文时,Java Oracle 不公开支持 7。
我有一个 FXML 控制器,它有一些 Spring Bean 依赖项。我找不到在加载控制器之前及时自动装配它们的方法,因为我使用的是自定义 FXML 加载器:
@Bean
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public UserProfile attachDocController() throws IOException {
return (UserProfile) loadController("/myproject/Forms/userProfile.fxml");
}
FXMLLoader loader = null;
protected Object loadController(String url) throws IOException {
loader = new FXMLLoader(getClass().getResource(url));
loader.load();
return loader.getController();
}
使用这种方法,我只能通过 @Autowired
注解直接注入来自动装配 bean:
public class UserProfile {
@Autowired
MyDependency myDependency;
这让我依赖于 Spring,并且以后会给我留下代码可维护性问题。如何将 Spring XML 文件配置中的依赖项自动装配到 FXML 控制器 class 中?类似于:
<bean id="UserProfile" class="myproject.controllerinjection.UserProfile" scope="prototype">
<aop:scoped-proxy proxy-target-class="true"/>
<property name="myDependency" ref="myDependency" />
</bean>
<bean id="myDependency" class="myproject.controllerinjection.MyDependency" scope="prototype">
<aop:scoped-proxy proxy-target-class="true"/>
</bean>
随着项目变大,考虑到长期项目可维护性,这似乎是一条更好的路线。
更新:
我不太习惯 Lambda 表达式。我研究了一下,但是整合@James_D的建议如下:
protected Object loadBeanController(String url) throws IOException {
loader = new FXMLLoader(getClass().getResource(url));
ApplicationContext ctx = WakiliProject.getCtx();
if (ctx != null) {
System.out.println("Load Bean...............");
loader.setControllerFactory(ctx::getBean);
} else {
System.out.println("No App.ctx...............");
}
return loader.getController();
}
每当我尝试调用 MyDependency
的方法时, 都会给出一个空指针。 MyDependency myDependency
永远不会注入 UserProfile
.
当您调用 FXMLLoader.load()
时,它会加载 FXML 文件。如果根元素中有 fx:controller
属性,它会根据指定的 class 创建一个控制器(并将 fx:id
属性元素注入该控制器实例,等等)。然后加载程序 returns FXML 文件的根目录。控制器本质上链接到 FXML 根。
默认情况下,FXMLLoader
通过反射将控制器class映射到一个实例,调用controllerClass.newInstance()
(这有效地调用了控制器的无参数构造函数class).您可以通过在 FXMLLoader
.
controllerFactory
来配置它,覆盖默认行为
controllerFactory
是一个将 Class<?>
对象(根据 fx:controller
属性中指定的 class 名称构造)映射到控制器实例的函数。如果您使用 Spring 来管理您的控制器实例,您只需要此函数请求 Spring 应用程序上下文(bean 工厂)为您生成控制器实例。所以你基本上可以做 fxmlLoader.setControllerFactory(applicationContext::getBean);
。使用此设置,只需通过 FXMLLoader
加载 fxml 文件将导致 FXMLLoader
从应用程序上下文请求控制器 class。可以使用 Spring 允许的任何方式配置应用程序上下文。
所以你的配置看起来像
@Configuration
public class Config {
@Bean
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public UserProfile attachDocController() throws IOException {
return new UserProfile();
}
}
当然,您现在可以在配置中注入依赖项class:
@Bean
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "prototype")
public UserProfile attachDocController(MyDependency myDependency) throws IOException {
return new UserProfile(myDependency);
}
@Bean
public MyDependency createDependency() {
return new MyDependencyImpl();
}
然后在你的 UI 工作中你可以做到
FXMLLoader loader = new FXMLLoader(getClass().getResource("/myproject/Forms/userProfile.fxml"));
loader.setControllerFactory(applicationContext::getBean);
Parent root = loader.load();
// since everything can be initialized in the controller by D.I., you
// shouldn't need to access it, but if you do for some reason you can do
UserProfile controller = loader.getController();
其中 applicationContext
是您的 Spring 应用程序上下文。无论应用程序上下文使用 XML 配置、基于注释的配置还是 Java 配置,这都有效。
更新
如果由于某种原因您不能使用 Java 8 或更高版本,则调用 setControllerFactory
与 Java 7 兼容,如下所示:
loader.setControllerFactory(new Callback<Class<?>, Object>() {
@Override
public Object call(Class<?> c) {
return applicationContext.getBean(c);
}
});
您需要 applicationContext
成为字段或 final
局部变量才能在 Java 7 中工作。请注意,在撰写本文时,Java Oracle 不公开支持 7。