我想在控制器的initialize
方法在创建时自动调用之前在控制器中设置一些非UI字段。据我了解,这样做的方法是提供自定义的ControllerFactory
,因为initialize()
在ControllerFactory
返回创建的对象之后被调用。我想根据this答案使用以下代码:
FXMLLoader loader = new FXMLLoader(mainFXML); // some .fxml file to load
loader.setControllerFactory(param -> {
Object controller = null;
try {
controller = ReflectUtil.newInstance(param); // this is default behaviour
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
if (controller instanceof Swappable) {
((Swappable) controller).setSwapper(swapper); // this is what I want to add
}
return controller;
});
但是,
ReflectUtil
类(在默认的setControllerFactory
method中使用)是com.sun.reflect.misc
包的一部分,我无法使用它,因为error: package sun.reflect.misc does not exist
编译失败。据我了解,我不能使用sun软件包,因为它不是公共API。所以问题是:我该怎么办?我找不到任何其他示例,只有那些带有ReflectUtil的示例,好吧,我希望我的
ControllerFactory
遵循带有@FXML批注的JavaFX的默认工作流,所有其他类似的DI框架都可以做到这一点例如Jodd Petite?还有其他设置字段的方法吗? (除了对其进行同步并在initialize()
中等待,直到从其他线程调用setter方法为止)。在github上的Full code作为上下文。
最佳答案
如果要通过反射创建实例,则需要使用Class.getConstructor(Class...)
1,然后使用Constructor.newInstance(Object...)
。
FXMLLoader loader = new FXMLLoader(/* some location */);
loader.setControllerFactory(param -> {
Object controller;
try {
controller = param.getConstructor().newInstance();
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
if (controller instanceof Swappable) {
((Swappable) controller).setSwapper(swapper);
}
return controller;
}
此代码要求您的控制器类具有公共的,无参数的构造函数。如果要通过构造函数注入依赖项,则可以执行以下操作:
FXMLLoader loader = new FXMLLoader(/* some location */);
loader.setControllerFactory(param -> {
Object controller;
try {
if (Swappable.class.isAssignableFrom(param)) {
controller = param.getConstructor(Swapper.class).newInstance(swapper);
} else {
controller = param.getConstructor().newInstance();
}
} catch (ReflectiveOperationException ex) {
throw new RuntimeException(ex);
}
return controller;
}
此代码假定
Swappable
的所有子类都有一个采用Swapper
的公共单参数构造函数。如果要获取非公共构造函数,则需要使用
Constructor.getDeclaredConstructor(Class...)
。然后,您需要在调用setAccessible(true)
之前调用Constructor
。如果使用Jigsaw模块(Java 9+),并且此控制器工厂代码与控制器类不在同一模块中,则要记住一些事情。假设控制器工厂代码在模块
foo
中,而控制器类在模块bar
中:如果将公共控制器与公共构造函数一起使用,则
bar
必须exports
控制器类的程序包至少包含foo
如果使用非公共控制器和/或构造函数,则必须发生相同的事情,但使用
opens
而不是exports
否则将引发异常。
1.如果使用无参数(不一定是公共)构造函数,则可以绕过
getConstructor
并直接调用Class.newInstance()
。但是,请注意,此方法存在问题,并且自Java 9开始不推荐使用。