使用 Guice 将数据注入 JavaFX ViewController
Inject Data with Guice into JavaFX ViewController
今天我将 Guice 添加到我的 Java FX 应用程序中。主要目标是用注入替换我的单例并分解依赖关系。
到目前为止一切正常,这是我必须开始一个新场景的代码:
public class App extends Application{
public static void main(String[] args){
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
final String LANGUAGE_BUNDLE = "myBundlePath";
final String FXML = "myFXMLPath";
try {
ResourceBundle resourceBundle = ResourceBundle.getBundle(LANGUAGE_BUNDLE, Locale.GERMAN, this.getClass().getClassLoader());
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(FXML), resourceBundle, new JavaFXBuilderFactory(), getGuiceControllerFactory());
primaryStage.setScene(new Scene(fxmlLoader.load()));
primaryStage.show();
}catch (IOException ex) {
ex.printStackTrace();
}
}
private Callback<Class<?>, Object> getGuiceControllerFactory(){
Injector injector = Guice.createInjector(new GuiceModule());
return new Callback<Class<?>, Object>() {
@Override
public Object call(Class<?> clazz) {
return injector.getInstance(clazz);
}
};
}
}
我的 Guice 模块如下所示:
public class GuiceModule extends AbstractModule {
@Override
protected void configure() {
bind(ITester.class).to(Tester.class);
bind(ISecondTest.class).to(SecondTest.class);
}
}
请注意,我替换了资源包和 fxml 文件的路径,因为它们会暴露我的身份。但是加载和一切正常,所以这应该不是问题 ;)
现在的问题是,我想通过在不同视图中单击按钮来实例化一个新视图。第二个视图应该显示视图 1 中数据的更详细版本。
我需要传递给第二个视图的所有内容都是整数(或 int),但我完全不知道如何执行此操作。
我有标准的 FX 设置:
View.fxml (with a reference to the ViewController)
ViewController.java
Model.java
ViewModel.java
然后 ViewController
绑定到 ViewModel
提供的属性。
我需要 int 来选择正确的型号。
我能找到的所有内容都是关于注释的@Named
,但据我所知,这不能用于注入动态数据。
你能给我提示一下我想做的这个叫什么吗?
或者长话短说:如何在一秒钟内注入一个由不同视图选择的变量 ViewController
,然后将其余部分保留在标准 FX 方式中?
感谢任何帮助并提前致谢!
此致,克里斯蒂安
经过多方尝试,看来我自己找到了解决办法!
但是,它 "feels" 我正在做的事情很丑陋,所以我想得到一些确认 ;)
首先是理论:Guice 支持 "AssistedInject"。这是当 class 不能由默认构造函数构造时。为了能够使用 AssistedInject,您必须下载扩展程序(我从 maven repository 下载了 jar)。
AssistedInject 为您做的是它允许您指定一个工厂来为您构建变量。所以这就是我所做的:
首先,为稍后要使用的 class 创建一个接口,在我的例子中:
public interface IViewController {
}
其次,为工厂创建一个接口。重要提示:您不必实施工厂
public interface IControllerFactory {
ViewController create(@Assisted int myInt);
}
第三,将具有相应参数的构造函数添加到您稍后要实例化的class,并让它实现您创建的接口,如下所示:
public class ViewController implements IViewController{
@AssistedInject
public ViewController(@Assisted int i){
final String LANGUAGE_BUNDLE = "languageBundle";
final String FXML = "View.fxml";
try{
ResourceBundle resourceBundle = ResourceBundle.getBundle(LANGUAGE_BUNDLE, Locale.GERMAN, this.getClass().getClassLoader());
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(FXML), resourceBundle, new JavaFXBuilderFactory());
fxmlLoader.setController(this);
Stage second = new Stage();
second.setScene(new Scene(fxmlLoader.load()));
second.show();
}catch (IOException e){
e.printStackTrace();
}
System.out.println("ViewController constructor called with: " + i);
}
这里有几点需要注意:
- 方法的注解“@AssistedInject”
- 我们要对外提供的参数的注解“@Assisted”
- 我们手动为装载机设置控制器(使用 fxmlLoader.setController(this);)
- 我必须删除 fxml 文件中的控制器配置,所以 fxml 中没有 "fx:controller"!
接下来我们需要将一个变量添加到 class 从我们想要实例化另一个 class:
@Inject
IControllerFactory controllerFactory;
我们可以像这样在class中使用它:
controllerFactory.create(3)
注意:我们调用了 ViewController class 中从未实现过的方法 "create"! Guice 知道它必须调用构造函数 - magic
作为最后一步,我们需要在我们的 GuiceModule 中添加到上下文的连接,如下所示:
@Override
protected void configure(){
install(new FactoryModuleBuilder()
.implement(IPagingDirectoryViewController.class, PagingDirectoryViewController.class)
.build(IPagingDirectoryControllerFactory.class));
}
注意 我收到错误:无法解析方法 'implement java.lang.Class<"The interface class">, java.lang.Class<"The implementing class">'。这是因为我忘记让我的控制器 class 实现接口。
好的,这就是我让它工作的方式。
然而,正如我所说,我很乐意听取一些意见!
此致,克里斯蒂安
在您的模块配置中,您可以简单地为 FXMLLoader 添加一个 Provider 方法,在该方法中您将 Guices 'injector.getInstance()' 指定为加载器的 ControllerFactory。
@Provides
public FXMLLoader getFXMLLoader(com.google.inject.Injector injector) {
FXMLLoader loader = new FXMLLoader();
loader.setControllerFactory(injector::getInstance);
return loader;
}
您现在要做的就是在模块配置的 configure() 方法中绑定 ViewController。
// for example:
bind(ViewController.class);
并确保控制器 class 已正确绑定到您的 fxml 文件中。
fx:controller="your.package.ViewController"
现在您只需使用注入器来获取 FXMLLoader 的实例。
今天我将 Guice 添加到我的 Java FX 应用程序中。主要目标是用注入替换我的单例并分解依赖关系。 到目前为止一切正常,这是我必须开始一个新场景的代码:
public class App extends Application{
public static void main(String[] args){
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
final String LANGUAGE_BUNDLE = "myBundlePath";
final String FXML = "myFXMLPath";
try {
ResourceBundle resourceBundle = ResourceBundle.getBundle(LANGUAGE_BUNDLE, Locale.GERMAN, this.getClass().getClassLoader());
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(FXML), resourceBundle, new JavaFXBuilderFactory(), getGuiceControllerFactory());
primaryStage.setScene(new Scene(fxmlLoader.load()));
primaryStage.show();
}catch (IOException ex) {
ex.printStackTrace();
}
}
private Callback<Class<?>, Object> getGuiceControllerFactory(){
Injector injector = Guice.createInjector(new GuiceModule());
return new Callback<Class<?>, Object>() {
@Override
public Object call(Class<?> clazz) {
return injector.getInstance(clazz);
}
};
}
}
我的 Guice 模块如下所示:
public class GuiceModule extends AbstractModule {
@Override
protected void configure() {
bind(ITester.class).to(Tester.class);
bind(ISecondTest.class).to(SecondTest.class);
}
}
请注意,我替换了资源包和 fxml 文件的路径,因为它们会暴露我的身份。但是加载和一切正常,所以这应该不是问题 ;)
现在的问题是,我想通过在不同视图中单击按钮来实例化一个新视图。第二个视图应该显示视图 1 中数据的更详细版本。 我需要传递给第二个视图的所有内容都是整数(或 int),但我完全不知道如何执行此操作。
我有标准的 FX 设置:
View.fxml (with a reference to the ViewController)
ViewController.java
Model.java
ViewModel.java
然后 ViewController
绑定到 ViewModel
提供的属性。
我需要 int 来选择正确的型号。
我能找到的所有内容都是关于注释的@Named
,但据我所知,这不能用于注入动态数据。
你能给我提示一下我想做的这个叫什么吗?
或者长话短说:如何在一秒钟内注入一个由不同视图选择的变量 ViewController
,然后将其余部分保留在标准 FX 方式中?
感谢任何帮助并提前致谢!
此致,克里斯蒂安
经过多方尝试,看来我自己找到了解决办法! 但是,它 "feels" 我正在做的事情很丑陋,所以我想得到一些确认 ;)
首先是理论:Guice 支持 "AssistedInject"。这是当 class 不能由默认构造函数构造时。为了能够使用 AssistedInject,您必须下载扩展程序(我从 maven repository 下载了 jar)。
AssistedInject 为您做的是它允许您指定一个工厂来为您构建变量。所以这就是我所做的:
首先,为稍后要使用的 class 创建一个接口,在我的例子中:
public interface IViewController {
}
其次,为工厂创建一个接口。重要提示:您不必实施工厂
public interface IControllerFactory {
ViewController create(@Assisted int myInt);
}
第三,将具有相应参数的构造函数添加到您稍后要实例化的class,并让它实现您创建的接口,如下所示:
public class ViewController implements IViewController{
@AssistedInject
public ViewController(@Assisted int i){
final String LANGUAGE_BUNDLE = "languageBundle";
final String FXML = "View.fxml";
try{
ResourceBundle resourceBundle = ResourceBundle.getBundle(LANGUAGE_BUNDLE, Locale.GERMAN, this.getClass().getClassLoader());
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(FXML), resourceBundle, new JavaFXBuilderFactory());
fxmlLoader.setController(this);
Stage second = new Stage();
second.setScene(new Scene(fxmlLoader.load()));
second.show();
}catch (IOException e){
e.printStackTrace();
}
System.out.println("ViewController constructor called with: " + i);
}
这里有几点需要注意:
- 方法的注解“@AssistedInject”
- 我们要对外提供的参数的注解“@Assisted”
- 我们手动为装载机设置控制器(使用 fxmlLoader.setController(this);)
- 我必须删除 fxml 文件中的控制器配置,所以 fxml 中没有 "fx:controller"!
接下来我们需要将一个变量添加到 class 从我们想要实例化另一个 class:
@Inject
IControllerFactory controllerFactory;
我们可以像这样在class中使用它:
controllerFactory.create(3)
注意:我们调用了 ViewController class 中从未实现过的方法 "create"! Guice 知道它必须调用构造函数 - magic
作为最后一步,我们需要在我们的 GuiceModule 中添加到上下文的连接,如下所示:
@Override
protected void configure(){
install(new FactoryModuleBuilder()
.implement(IPagingDirectoryViewController.class, PagingDirectoryViewController.class)
.build(IPagingDirectoryControllerFactory.class));
}
注意 我收到错误:无法解析方法 'implement java.lang.Class<"The interface class">, java.lang.Class<"The implementing class">'。这是因为我忘记让我的控制器 class 实现接口。
好的,这就是我让它工作的方式。 然而,正如我所说,我很乐意听取一些意见!
此致,克里斯蒂安
在您的模块配置中,您可以简单地为 FXMLLoader 添加一个 Provider 方法,在该方法中您将 Guices 'injector.getInstance()' 指定为加载器的 ControllerFactory。
@Provides
public FXMLLoader getFXMLLoader(com.google.inject.Injector injector) {
FXMLLoader loader = new FXMLLoader();
loader.setControllerFactory(injector::getInstance);
return loader;
}
您现在要做的就是在模块配置的 configure() 方法中绑定 ViewController。
// for example:
bind(ViewController.class);
并确保控制器 class 已正确绑定到您的 fxml 文件中。
fx:controller="your.package.ViewController"
现在您只需使用注入器来获取 FXMLLoader 的实例。