正如你所猜测的那样
但这将导致所有项目共享同一个控制器,不是吗?
默认情况下,Spring for each Bean创建一个实例,并在对该Bean发出请求时共享相同的实例.这个行为由Springscopes控制,这个默认作用域是singleton作用域.要在每个请求上创建一个新的Bean类实例,您需要prototype作用域.
您可以(也确实应该这样做,因为每次加载FXML时都应该创建一次JavaFX控制器)通过注释控制器类@Scope("prototype")
告诉Spring在每次请求控制器时创建一个新实例:
@Component
@Scope("prototype")
public class MyControllerClass {
// ...
}
这里有一个非常快速的例子.这是一个FXML,我称它为Adder.fxml
.它包含两个Spinner<Integer>
和一个HBox
中的Label
.其 idea 是标签将在微处理器中显示这两个值的总和.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Spinner?>
<?import javafx.scene.layout.HBox?>
<HBox spacing="20.0" xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jamesd.examples.springscope.AddingController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
</padding>
<Spinner fx:id="firstSummand" ></Spinner>
<Spinner fx:id="secondSummand"></Spinner>
<Label fx:id="sumText" HBox.hgrow="ALWAYS"/>
</HBox>
这是控制器.请注意,它是否同时带有@Component
和@Scope("prototytpe")
的注释.
package org.jamesd.examples.springscope;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.control.SpinnerValueFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Scope("prototype")
public class AddingController {
@FXML
private Label sumText;
@FXML
private Spinner<Integer> firstSummand;
@FXML
private Spinner<Integer> secondSummand;
@FXML
private void initialize() {
List.of(firstSummand, secondSummand).forEach(spinner -> {
spinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(-20, 20, 0));
spinner.valueProperty().addListener(obs -> updateSum());
});
updateSum();
}
@FXML
private void updateSum() {
sumText.setText(Integer.toString(firstSummand.getValue() + secondSummand.getValue()));
}
}
下面是一个Spring bootstrap 应用程序类,旨在启动一个JavaFX应用程序:
package org.jamesd.examples.springscope;
import javafx.application.Application;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringApp {
public static void main(String[] args) {
Application.launch(HelloApplication.class, args);
}
}
和FX应用程序:
package org.jamesd.examples.springscope;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import java.io.IOException;
public class HelloApplication extends Application {
private ConfigurableApplicationContext applicationContext;
@Override
public void init() {
applicationContext = new SpringApplicationBuilder(SpringApp.class).run();
}
@Override
public void start(Stage stage) throws IOException {
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
for (int i = 0 ; i < 3; i++) {
root.getChildren().add(createAdder());
}
Scene scene = new Scene(root, 600, 375);
stage.setTitle("Adding");
stage.setScene(scene);
stage.show();
}
private Node createAdder() throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("Adder.fxml"));
fxmlLoader.setControllerFactory(applicationContext::getBean);
return fxmlLoader.load();
}
}
该应用程序创建在Adder.fxml
中定义的三个UI实例,它们独立工作.
如果您删除控制器中的@Scope("prototype")
注释,您将看到它无法正常工作.(由于每次加载FXML时字段都被重新分配给同一个控制器实例,因此只有最后加载的实例才会更新.)
注您还可以使用Springmeta-annotations来创建您自己的元注释,它表示@Component
,它始终包含@Scope("prototype")
:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@Scope("prototype")
public @interface FXController {}
然后你就可以做
@FXController
public class AddingController {
// ...
}
这可能是值得的,因为spring管理的应用程序中的FX控制器应该是原型范围.