JavaFx自定义控件和Spring DI



我正在尝试将自定义控件添加到我在这里找到的示例中:https://spring.io/blog/2019/01/16/spring-tips-javafx(代码:https://github.com/ecovaci/javafx-spring-boot/tree/master)

当然,我的目标是能够在我的控制器中自动连接业务服务:

@Service
public class TestService {
public void test() {
System.out.println("test");
}
}

我可以通过在非自定义控件中注入构造函数来做到这一点,比如:

@Component
public class SimpleUiController {
private final HostServices hostServices;
@FXML
public Label label;
@FXML
public Button button;
private TestService testService;
public SimpleUiController(HostServices hostServices, TestService testService) {
Assert.notNull(testService);
this.hostServices = hostServices;
this.testService = testService;
}
@FXML
public void initialize () {
this.button.setOnAction(actionEvent -> this.testService.test());
}
}

我接下来想要的是在fxml中添加一个带有附带控制器的自定义控件。所以我放弃了,天真地复制了这里的代码片段:https://docs.oracle.com/javafx/2/fxml_get_started/custom_control.htm

<!-- custom_control.fxml -->
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml">
<TextField fx:id="textField"/>
<Button text="Click Me" />
</fx:root>
@Component
public class CustomControl extends VBox {
@FXML
private TextField textField;
private TestService testService;
public CustomControl(TestService testService) {
Assert.notNull(testService);
this.testService = testService;
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/custom_control.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public String getText() {
return textProperty().get();
}
public void setText(String value) {
textProperty().set(value);
}
public StringProperty textProperty() {
return textField.textProperty();
}
}

现在我可以像这样使用我的自定义控件:

<!--ui.fxml-->
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.example.javafx.SimpleUiController">
<Button fx:id="button" text="Button" />
<Label fx:id="label" text="Label" />
<CustomControl text="100"/>
</VBox>

但是当我添加自定义控件时,我会得到以下异常。

Exception in Application start method
Caused by: java.lang.NoSuchMethodException: com.example.javafx.CustomControl.<init>()

我是JavaFx的新手,很高兴能找到一些关于与Spring集成的文章。但我没有找到任何关于自定义控件和spring的参考资料。感觉好像只有我一个人在努力实现这一点,我是不是错过了什么?

按照James_D的建议,添加了一个自定义生成器工厂,从而解决了这个问题。

@Component
public class BeanBuilderFactory implements BuilderFactory {
@Autowired
private ConfigurableApplicationContext context;
@Override
public Builder<?> getBuilder(Class<?> type) {
try {
Object bean = this.context.getBean(type);
if (bean.getClass().isAssignableFrom(type)) {
if (bean instanceof CustomControl) {
return new CustomControlBuilder((CustomControl) bean);
}
}
JavaFXBuilderFactory javaFXBuilderFactory = new JavaFXBuilderFactory();
return javaFXBuilderFactory.getBuilder(bean.getClass());
} catch (BeansException e) {
return null;
}
}

我不得不为我的自定义组件创建一个生成器。我想也有机会使用反思,但我没有做到这一点。

public class CustomControlBuilder implements Builder<CustomControl> {
private CustomControl customControl;
public CustomControlBuilder(CustomControl customControl) {
this.customControl = customControl;
}
public String getText() {
return customControl.getText();
}
public void setText(String value) {
customControl.setText(value);
}
@Override
public CustomControl build() {
return customControl;
}
}

这是我添加建筑商工厂的原始示例中的stageListener:

@Component
public class StageListener implements ApplicationListener<JavafxApplication.StageReadyEvent> {
private final String applicationTitle;
private final Resource fxml;
private final ApplicationContext applicationContext;
private final BeanBuilderFactory beanBuilderFactory;
public StageListener(@Value("${spring.application.ui.title}") String applicationTitle,
@Value("classpath:/ui.fxml") Resource fxml, ApplicationContext applicationContext,
BeanBuilderFactory aBeanBuilderFactory) {
this.applicationTitle = applicationTitle;
this.fxml = fxml;
this.applicationContext = applicationContext;
this.beanBuilderFactory = aBeanBuilderFactory;
}
@Override
public void onApplicationEvent(JavafxApplication.StageReadyEvent stageReadyEvent) {
try {
Stage stage = stageReadyEvent.getStage();
URL url = fxml.getURL();
FXMLLoader fxmlLoader = new FXMLLoader(url);
fxmlLoader.setControllerFactory(applicationContext::getBean);
fxmlLoader.setBuilderFactory(beanBuilderFactory);
Parent root = fxmlLoader.load();
Scene scene = new Scene(root, 600, 600);
stage.setScene(scene);
stage.setTitle(this.applicationTitle);
stage.show();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

最新更新