通过控制器工厂传递构造函数参数的问题
Issue with passing constructor parameter via controller factory
我在向控制器传递参数时遇到了问题。主程序使用控制器工厂将阶段对象传递给我的控制器。控制器工厂打印阶段对象的地址,但控制器获取 Null 作为阶段对象。为什么?
虽然我已经将我的应用程序简化为一个简单的对话,但我找不到我的错。我希望你能帮忙。谢谢!
主程序:
public class Main extends Application {
private final Logger logger = Logger.getLogger(this.getClass().getName());
private final String FXML_SIMPLE_DIALOG = "testDialog.fxml";
private MyHandler myHandler = new MyHandler();
@Override
public void init() {
try{
// Thread.currentThread is the FX-Launcher thread:
Thread.currentThread().setUncaughtExceptionHandler(myHandler);
try {
logger.addHandler(new FileHandler("java.myLOG"));
}
catch (IOException e) {
throw new IllegalStateException("IOException when adding File Handler");
}
}
catch (Exception ex) {
myHandler.uncaughtException(Thread.currentThread(), ex);
throw(ex);
}
}
@Override
public void start(Stage primaryStage) {
try{
logger.info("Test Application started");
Thread.currentThread().setUncaughtExceptionHandler(myHandler);
try{
URL location = new URL(this.getClass().getResource("resources/fxml/" + FXML_SIMPLE_DIALOG).toString());
FXMLLoader loader = new FXMLLoader(location);
loader.setControllerFactory(new SimpleControllerFactory(primaryStage));
Pane root = loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("*** TEST App ***");
primaryStage.show();
}
catch(IOException ex) {
ex.printStackTrace();
throw new IllegalStateException("check program source code!");
}
}
catch(Exception ex) {
myHandler.uncaughtException(Thread.currentThread(), ex);
throw(ex);
}
}
public static void main(String[] args) {
launch(args);
}
class MyHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
logger.log(Level.SEVERE, "** STOPP ** due to uncaught exception", throwable);
Platform.exit();
}
}
}
控制器出厂代码:
public class SimpleControllerFactory implements Callback<Class<?>,Object> {
private static final Logger logger = Logger.getLogger(SimpleControllerFactory.class.getName());
private final Stage primaryStage;
public SimpleControllerFactory(Stage stage) {
this.primaryStage = stage;
System.out.println("controller factory: value of stage: " + this.primaryStage);
}
public SimpleControllerFactory() { this(null); }
@Override
public Object call(Class<?> type) {
try {
for (var c : type.getConstructors()) {
switch(c.getParameterCount()) {
case 0 -> {}
case 1 -> {
if ( c.getParameterTypes()[0] == Stage.class) {
return c.newInstance(primaryStage) ;
}
else;
}
default -> {}
}
}
return type.getDeclaredConstructor().newInstance();
}
catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException ex) {
logger.log(Level.SEVERE,ex.toString(),ex);
throw new RuntimeException(ex);
}
}
}
这是 FXML 文件和简单的控制器:
<VBox prefHeight="150.0" prefWidth="250.0" spacing="5.0" style="-fx-padding: 5 5 5 5;-fx-font-size: 11px"
xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="TestSimpleDialog.DialogController">
<children>
<ButtonBar buttonOrder="UL_HE+FBIX_NCYOA_R" prefHeight="40.0">
<buttons>
<Button alignment="CENTER" mnemonicParsing="false" onAction="#time" text="time ?">
</Button>
</buttons>
</ButtonBar>
<StackPane VBox.vgrow="ALWAYS">
<children>
<Label fx:id="textTime" alignment="CENTER" text="" />
</children>
</StackPane>
</children>
简单控制器:
public class DialogController implements Initializable {
private static final Logger logger = Logger.getLogger(DialogController.class.getName());
private final Stage recentStage;
DialogController(Stage stage) {
this.recentStage = stage;
}
DialogController() { this(null); }
@FXML private Label textTime;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
System.out.println("Controller started, value of recentStage: " + this.recentStage);
}
@FXML
private void time(ActionEvent event) {
textTime.setText(LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy kk:mm:ss")));
}
}
Returns an array containing Constructor
objects reflecting all the public constructors of the class represented by this Class
object.
(我的重点)。
由于您的构造函数未声明 [=36=],因此工厂 call(...)
方法中的 for
循环迭代零次,并且默认调用 no-arg构造函数。
只需将构造函数声明为 public
即可:
public class DialogController implements Initializable {
private final Stage recentStage;
public DialogController(Stage stage) {
this.recentStage = stage;
}
public DialogController() { this(null); }
// ...
}
如果您真的想要保留构造函数non-public,您可以使用getDeclaredConstructor(...)
方法来检索特定的构造函数,而不必成为 public:
@Override
public Object call(Class<?> type) {
try {
System.out.println("Number of constructors found: "+type.getConstructors().length);
try {
Constructor<?> c = type.getDeclaredConstructor(Stage.class);
return c.newInstance(primaryStage);
} catch (NoSuchMethodException e) {
return type.getDeclaredConstructor().newInstance();
}
}
catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException ex) {
logger.log(Level.SEVERE,ex.toString(),ex);
throw new RuntimeException(ex);
}
}
我在向控制器传递参数时遇到了问题。主程序使用控制器工厂将阶段对象传递给我的控制器。控制器工厂打印阶段对象的地址,但控制器获取 Null 作为阶段对象。为什么?
虽然我已经将我的应用程序简化为一个简单的对话,但我找不到我的错。我希望你能帮忙。谢谢!
主程序:
public class Main extends Application {
private final Logger logger = Logger.getLogger(this.getClass().getName());
private final String FXML_SIMPLE_DIALOG = "testDialog.fxml";
private MyHandler myHandler = new MyHandler();
@Override
public void init() {
try{
// Thread.currentThread is the FX-Launcher thread:
Thread.currentThread().setUncaughtExceptionHandler(myHandler);
try {
logger.addHandler(new FileHandler("java.myLOG"));
}
catch (IOException e) {
throw new IllegalStateException("IOException when adding File Handler");
}
}
catch (Exception ex) {
myHandler.uncaughtException(Thread.currentThread(), ex);
throw(ex);
}
}
@Override
public void start(Stage primaryStage) {
try{
logger.info("Test Application started");
Thread.currentThread().setUncaughtExceptionHandler(myHandler);
try{
URL location = new URL(this.getClass().getResource("resources/fxml/" + FXML_SIMPLE_DIALOG).toString());
FXMLLoader loader = new FXMLLoader(location);
loader.setControllerFactory(new SimpleControllerFactory(primaryStage));
Pane root = loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("*** TEST App ***");
primaryStage.show();
}
catch(IOException ex) {
ex.printStackTrace();
throw new IllegalStateException("check program source code!");
}
}
catch(Exception ex) {
myHandler.uncaughtException(Thread.currentThread(), ex);
throw(ex);
}
}
public static void main(String[] args) {
launch(args);
}
class MyHandler implements Thread.UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
logger.log(Level.SEVERE, "** STOPP ** due to uncaught exception", throwable);
Platform.exit();
}
}
}
控制器出厂代码:
public class SimpleControllerFactory implements Callback<Class<?>,Object> {
private static final Logger logger = Logger.getLogger(SimpleControllerFactory.class.getName());
private final Stage primaryStage;
public SimpleControllerFactory(Stage stage) {
this.primaryStage = stage;
System.out.println("controller factory: value of stage: " + this.primaryStage);
}
public SimpleControllerFactory() { this(null); }
@Override
public Object call(Class<?> type) {
try {
for (var c : type.getConstructors()) {
switch(c.getParameterCount()) {
case 0 -> {}
case 1 -> {
if ( c.getParameterTypes()[0] == Stage.class) {
return c.newInstance(primaryStage) ;
}
else;
}
default -> {}
}
}
return type.getDeclaredConstructor().newInstance();
}
catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException ex) {
logger.log(Level.SEVERE,ex.toString(),ex);
throw new RuntimeException(ex);
}
}
}
这是 FXML 文件和简单的控制器:
<VBox prefHeight="150.0" prefWidth="250.0" spacing="5.0" style="-fx-padding: 5 5 5 5;-fx-font-size: 11px"
xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="TestSimpleDialog.DialogController">
<children>
<ButtonBar buttonOrder="UL_HE+FBIX_NCYOA_R" prefHeight="40.0">
<buttons>
<Button alignment="CENTER" mnemonicParsing="false" onAction="#time" text="time ?">
</Button>
</buttons>
</ButtonBar>
<StackPane VBox.vgrow="ALWAYS">
<children>
<Label fx:id="textTime" alignment="CENTER" text="" />
</children>
</StackPane>
</children>
简单控制器:
public class DialogController implements Initializable {
private static final Logger logger = Logger.getLogger(DialogController.class.getName());
private final Stage recentStage;
DialogController(Stage stage) {
this.recentStage = stage;
}
DialogController() { this(null); }
@FXML private Label textTime;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
System.out.println("Controller started, value of recentStage: " + this.recentStage);
}
@FXML
private void time(ActionEvent event) {
textTime.setText(LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy kk:mm:ss")));
}
}
Returns an array containing
Constructor
objects reflecting all the public constructors of the class represented by thisClass
object.
(我的重点)。
由于您的构造函数未声明 [=36=],因此工厂 call(...)
方法中的 for
循环迭代零次,并且默认调用 no-arg构造函数。
只需将构造函数声明为 public
即可:
public class DialogController implements Initializable {
private final Stage recentStage;
public DialogController(Stage stage) {
this.recentStage = stage;
}
public DialogController() { this(null); }
// ...
}
如果您真的想要保留构造函数non-public,您可以使用getDeclaredConstructor(...)
方法来检索特定的构造函数,而不必成为 public:
@Override
public Object call(Class<?> type) {
try {
System.out.println("Number of constructors found: "+type.getConstructors().length);
try {
Constructor<?> c = type.getDeclaredConstructor(Stage.class);
return c.newInstance(primaryStage);
} catch (NoSuchMethodException e) {
return type.getDeclaredConstructor().newInstance();
}
}
catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException ex) {
logger.log(Level.SEVERE,ex.toString(),ex);
throw new RuntimeException(ex);
}
}