我想在控制器的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开始不推荐使用。

07-24 09:27