使用Spring Boot启动JavaFX 2

我正在尝试使用JavaFX 2和Spring Boot创建新的应用程序,但到目前为止,由于MainPaneController的“root is null”,我的简单应用程序(如hello world)未运行。

MainPaneController类:

 public class MainPaneController implements Initializable { public static final String VIEW = "/fxml/Scene.fxml"; @FXML private Node root; @FXML private Label label; @PostConstruct public void init() { } public Node getRoot() { return root; } @FXML private void handleButtonAction(ActionEvent event) { System.out.println("You clicked me!"); label.setText("Hello World!"); } @Override public void initialize(URL url, ResourceBundle rb) { // TODO } } 

主类FxBootApplication:

 @SpringBootApplication public class FxBootApplication extends Application { private static String[] args; @Override public void start(final Stage stage) throws Exception { //Parent root = FXMLLoader.load(getClass().getResource("/fxml/Scene.fxml")); // Bootstrap Spring context here. ApplicationContext context = SpringApplication.run(FxBootApplication.class, args); MainPaneController mainPaneController = context.getBean(MainPaneController.class); Scene scene = new Scene((Parent) mainPaneController.getRoot()); // error here //Scene scene = new Scene(root); //scene.getStylesheets().add("/styles/Styles.css"); stage.setTitle("JavaFX and Maven"); stage.setScene(scene); stage.show(); } /** * The main() method is ignored in correctly deployed JavaFX application. * main() serves only as fallback in case the application can not be * launched through deployment artifacts, eg, in IDEs with limited FX * support. NetBeans ignores main(). * * @param args the command line arguments */ public static void main(String[] args) { FxBootApplication.args = args; launch(args); } } 

ApplicationConfiguration类:

 @Configuration public class ApplicationConfiguration { @Bean public MainPaneController mainPaneController() throws IOException { MainPaneController mpc = (MainPaneController) loadController(MainPaneController.VIEW); return mpc; } public  T loadController(String url) throws IOException { try (InputStream fxmlStream = getClass().getResourceAsStream(url)) { FXMLLoader loader = new FXMLLoader(getClass().getResource(url)); //FXMLLoader.load(url); loader.load(fxmlStream); return loader.getController(); } } } 

错误是我尝试通过controller.getRoot()获取Scene的根目录时

我按照这里提出的解决方案 – > JavaFX fxml – 如何使用嵌套自定义控件的Spring DI? 但最终根本不适合我。 我以前应该以某种方式初始化这个root吗?

不幸的是我没有找到解决方案的链接,这对我有用,但是:我有代码,我在一定程度上进行了测试。

首先,您需要Application类:

 package eu.dzim.yatafx; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import eu.dzim.yatafx.model.app.ApplicationModel; import eu.dzim.yatafx.spring.service.FXMLLoaderService; import eu.dzim.yatafx.util.Utils; import eu.dzim.yatafx.util.res.StringResource; import javafx.application.Application; import javafx.application.Platform; import javafx.fxml.FXMLLoader; import javafx.scene.Scene; import javafx.scene.layout.Pane; import javafx.stage.Stage; @Configuration @EnableAutoConfiguration @ComponentScan public class YataFXApplication extends Application implements CommandLineRunner { private static final Logger LOG = LogManager.getLogger(FileSyncFXApplication.class); @Override public void run(String... args) { // something to call prior to the real application starts? } private static String[] savedArgs; // locally stored Spring Boot application context private ConfigurableApplicationContext applicationContext; // we need to override the FX init process for Spring Boot @Override public void init() throws Exception { // set Thread name Thread.currentThread().setName("main"); // LOG.debug("Init JavaFX application"); applicationContext = SpringApplication.run(getClass(), savedArgs); applicationContext.getAutowireCapableBeanFactory().autowireBean(this); } // ... and close our context on stop of the FX part @Override public void stop() throws Exception { // LOG.debug("Stop JavaFX application"); super.stop(); applicationContext.close(); } protected static void launchApp(Class appClass, String[] args) { FileSyncFXApplication.savedArgs = args; Application.launch(appClass, args); } @Autowired private FXMLLoaderService mFXMLLoaderService; @Autowired private ApplicationModel mApplicationModel; @Override public void start(Stage primaryStage) { // set Thread name Thread.currentThread().setName("main-ui"); try { FXMLLoader loader = mFXMLLoaderService.getLoader(Utils.getFXMLResource("Root"), StringResource.getResourceBundle()); Pane root = loader.load(); Scene scene = new Scene(root, 1200, 800); scene.getStylesheets().add("/eu/dzim/filesyncfx/ui/application.css"); primaryStage.setScene(scene); primaryStage.setOnCloseRequest(windowEvent -> { LOG.debug("tear down JavaFX application"); // mApplicationModel.setLoggedIn(!mLoginService.logout()); // orderly shut down FX Platform.exit(); // But: there might still be a daemon thread left over from OkHttp (some async dispatcher) // so assume everything is fine and call System.exit(0) System.exit(0); }); primaryStage.show(); } catch (Exception e) { LOG.error(e.getMessage(), e); } } public static void main(String[] args) throws Exception { // SpringApplication.run(SampleSimpleApplication.class, args); savedArgs = args; Application.launch(FileSyncFXApplication.class, args); } } 

我使用org.springframework.boot:spring-boot-starter-parent:1.3.3.RELEASE作为基础。

注意我在这里自动assembly的FXMLLoaderService接口:

 package eu.dzim.yatafx.spring.service; import java.net.URL; import java.util.ResourceBundle; import javafx.fxml.FXMLLoader; public interface FXMLLoaderService { FXMLLoader getLoader(); FXMLLoader getLoader(URL location); FXMLLoader getLoader(URL location, ResourceBundle resourceBundle); } 

实现看起来像这样:

 package eu.dzim.yatafx.spring.service.impl; import java.net.URL; import java.util.ResourceBundle; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import eu.dzim.yatafx.spring.service.FXMLLoaderService; import javafx.fxml.FXMLLoader; import javafx.util.Callback; @Component @Scope("singleton") public class FXMLLoaderServiceImpl implements FXMLLoaderService { private static final Logger LOG = LogManager.getLogger(FXMLLoaderServiceImpl.class); @Autowired private ConfigurableApplicationContext context; @PostConstruct private void postConstruct() { LOG.debug("PostConstruct: set up " + getClass().getName()); } @Override public FXMLLoader getLoader() { FXMLLoader loader = new FXMLLoader(); loader.setControllerFactory(new Callback, Object>() { @Override public Object call(Class param) { return context.getBean(param); } }); return loader; } @Override public FXMLLoader getLoader(URL location) { FXMLLoader loader = new FXMLLoader(location); loader.setControllerFactory(new Callback, Object>() { @Override public Object call(Class param) { return context.getBean(param); } }); return loader; } @Override public FXMLLoader getLoader(URL location, ResourceBundle resourceBundle) { FXMLLoader loader = new FXMLLoader(location, resourceBundle); loader.setControllerFactory(new Callback, Object>() { @Override public Object call(Class param) { return context.getBean(param); } }); return loader; } @PreDestroy private void preDestroy() { LOG.debug("PreDestroy: tear down " + getClass().getName()); } } 

用法已经显示在Application类中:只需@Autowire服务并从那里创建子视图。 由于我几乎完全依赖于FXML,这个对我来说是重要的,因为我想在我的控制器中使用所有那些不错的DI内容。

最好的例子是全局应用程序,它包含我在控制器类中附加的一些JavaFX属性。

虽然显示的应用程序更像是一个存根(应用程序和FXML服务),但我有一个有趣的项目,我将这种方法用于并行开发的Web应用程序,即我在工作中开发的“Micro”服务上的REST。

希望代码足以让它在你身边发挥作用。 如果您有更多问题,请问问。

干杯,丹尼尔

编辑 :我认为代码中的错误只是FXML服务中的一部分。 我有这个注入ConfigurableApplicationContext,我用它来创建控制器。 你需要一个ControllerFactory。


有一些第三方示例可以帮助JavaFX和Spring Boot集成: