使用Spring Boot启动JavaFX 2 [英] Launching JavaFX 2 with Spring Boot

查看:910
本文介绍了使用Spring Boot启动JavaFX 2的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create new application with JavaFX 2 and Spring Boot, but so far my simple app (like hello world) isn't running because of "root is null" in MainPaneController.

MainPaneController类:

MainPaneController class:

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:

Main class 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, e.g., 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 class:

ApplicationConfiguration class:

@Configuration
public class ApplicationConfiguration {

    @Bean
    public MainPaneController mainPaneController() throws IOException {
        MainPaneController mpc = (MainPaneController) loadController(MainPaneController.VIEW);
        return mpc;
    }

    public <T> 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();
        }
    }

}

错误是当我试图通过来获取场景的根目录;

Error is while I am trying to get root for Scene by controller.getRoot();

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

I followed the solution proposed here -> JavaFX fxml - How to use Spring DI with nested custom controls? but eventually is not working for me at all. Should I somehow initialize this root before?

推荐答案

不幸的是我找不到链接到解决方案,这对我有用了......
但是:我有代码,我测试了一段时间。

Unfortunately I don't find the link to the solution, that works for me, anymore... But: I have the code, which I tested to some extend.

首先你需要你的Application类:

First you need your Application class:

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<? extends FileSyncFXApplication> 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 作为基础。

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

Note the FXMLLoaderService interface I autowired here:

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<Class<?>, 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<Class<?>, 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<Class<?>, 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类中:Just @Autowire服务并从那里创建子视图。由于我几乎完全依赖于FXML,这个对我来说是重要的,因为我想在我的控制器中使用所有那些不错的DI东西。

The usage is already displayed in the Application class: Just @Autowire the service and create the sub-views from there. Since I rely almost exclusivly on FXML, this one is importand to me, since I want to use all that nice DI stuff in my Controllers.

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

Best example is the global Application, wich holds some JavaFX properties I attach to in the controller classes.

虽然显示的应用程序更像是存根(应用程序和FXML服务),但我玩得很开心我将这种方法用于并行开发的Web应用程序的项目,就是我在工作中开发的Micro服务上的REST。

While the shown application is more a stub (the application and the FXML service), I had a fun project where I used this approach in concurrency to a parallel developed Web application, that was RESTing on a "Micro"service I developed at work.

希望代码足够了让它在你身边工作的例子。如果你有更多问题,请问问。

Hope the code is enough an example to get it to work on your side. Please just ask, if you have more questions.

干杯,
Daniel

Cheers, Daniel

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

Edit: I think the mistake in your code is simply the part in the FXML service. I have this injected ConfigurableApplicationContext which I use to create the controller from. You would need a ControllerFactory for this.

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

There are some third party samples to assist with JavaFX and Spring Boot integration:

  • https://github.com/thomasdarimont/spring-labs/tree/master/spring-boot-javafx
  • https://github.com/thomasdarimont/spring-labs/tree/master/spring-boot-javafx-jpa-demo
  • http://springboot-javafx-support.readthedocs.io/en/latest/
  • https://github.com/bgmf/poc/tree/master/spring-boot-fx

这篇关于使用Spring Boot启动JavaFX 2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆