Ответ 1
Основная проблема здесь - убедиться, что Spring инициализируется в том же потоке приложения JavaFX. Обычно это означает, что код Spring должен выполняться в потоке приложения JavaFX; другие трудоемкие задания, конечно же, могут выполняться на их собственной ветке.
Это решение, которое я собрал, используя этот учебник и свои собственные знания Spring Загрузка:
@SpringBootApplication
@ImportResource("classpath:root-context.xml")
public class JavaFXSpringApplication extends Application {
private static final Logger log = LoggerFactory.getLogger(JavaFXSpringApplication.class);
private Messages messages;
private static String[] args;
@Override
public void start(final Stage primaryStage) {
// Bootstrap Spring context here.
ApplicationContext context = SpringApplication.run(JavaFXSpringApplication.class, args);
messages = context.getBean(Messages.class);
MainPaneController mainPaneController = context.getBean(MainPaneController.class);
// Create a Scene
Scene scene = new Scene((Parent) mainPaneController.getRoot());
scene.getStylesheets().add(getClass().getResource("/css/application.css").toExternalForm());
// Set the scene on the primary stage
primaryStage.setScene(scene);
// Any other shenanigans on the primary stage...
primaryStage.show();
}
public static void main(String[] args) {
JavaFXSpringApplication.args = args;
launch(args);
}
}
Этот класс является как точкой входа приложения JavaFX, так и точкой входа инициализации загрузки Spring, следовательно, происходит переход от varargs. Импорт внешнего файла конфигурации упрощает сохранение основного класса при использовании других Spring связанных компонентов (т.е. Настройка Spring данных JPA, пакетов ресурсов, безопасности...)
В методе "запуска" JavaFX основной ApplicationContext инициализируется и живет. Любые bean, используемые в этой точке, должны быть получены через ApplicationContext.getBean(), но каждый другой аннотированный bean (если он находится в потоковом пакете этого основного класса) будет доступен, как всегда.
В частности, контроллеры объявляются в этом другом классе:
@Configuration
@ComponentScan
public class ApplicationConfiguration {
@Bean
public MainPaneController mainPaneController() throws IOException {
return (MainPaneController) this.loadController("path/to/MainPane.fxml");
}
protected Object loadController(String url) throws IOException {
InputStream fxmlStream = null;
try {
fxmlStream = getClass().getResourceAsStream(url);
FXMLLoader loader = new FXMLLoader();
loader.load(fxmlStream);
return loader.getController();
} finally {
if (fxmlStream != null) {
fxmlStream.close();
}
}
}
}
Вы можете увидеть любой контроллер (у меня есть только один, но его может быть много) аннотируется с помощью @ Bean, а весь класс - это Конфигурация.
Наконец, вот MainPaneController.
public class MainPaneController {
@Autowired
private Service aService;
@PostConstruct
public void init() {
// ...stuff to do with components...
}
/*
* FXML Fields
*/
@FXML
private Node root;
@FXML
private TextArea aTextArea;
@FXML
private TextField aTextField;
@FXML
private void sayButtonAction(ActionEvent event) {
aService.doStuff(aTextArea, aTextField);
}
}
Этот контроллер объявлен как @ Bean, поэтому он может быть @Autowired с и из любого другого @Beans (или служб, компонентов и т.д.). Теперь, например, вы можете получить ответ на нажатие кнопки и делегировать логику, выполняемую в своих полях, на @Service. Любой компонент, объявленный в Spring -созданных контроллерах, будет управляться Spring и, таким образом, знать об этом контексте.
Все довольно легко и просто настроить. Не стесняйтесь спрашивать, есть ли у вас какие-либо сомнения.