JavaFX桌面开发——进阶- SpringBoot 整合

2,066 阅读4分钟

前言

到了这一步,JavaFX 的锋芒终于是显露出来了,可以和 SpringBoot 整合的话,那么就有了无限的潜力。技术更强的开发者想必是知道SpringBoot的威名的,这篇文章中,我将介绍如何让JavaFX 成功地融入 SpringBoot.

如何设置启动类

我们知道,SpringBoot程序 的启动,需要依赖于@SpringApplication注解,以及对应的 main方法中的调用内容:

 SpringApplication.run(启动类.class, args);

JavaFX 的启动方式是 继承 Application 抽象类,然后在start方法中启动。 所以正确的设置方案应该为:

启动方式

在 SpringBoot启动类的main方法中通过以下语句来启动 JavaFX。

Application.launch(JavaFX启动类,args); 

整个main方法看起来像这样:

public static void main(String[] args) {
     SpringApplication.run(DemoApplication.class,args);
       Application.launch(JavaFX启动类.class,args);
   }

这里 JavaFx 启动类可以是SpringBoot启动类

即DemoApplication 可以在当 SpringBoot启动类的前提下 继承Application 充当JavaFX 启动类

如何注入容器对象

在JavaFX 的FXML+ Controller 的进阶开发中,

按照惯性的思维,我们可以在Controller上添加@Component注解,然后通过`@Autowired```去注入对象,以下面代码为例,展示这种情况的结果。

@Component
@FXMLController
public class LoginController implements Initializable {
    @FXML
    public TextField username;

    @FXML
    public PasswordField password;
    public Button loginButton;

    @Autowired
    private UserService uService;
    public void userLogin(ActionEvent actionEvent) throws IOException {

        String username = this.username.getText();
        String userPassword = this.password.getText();

            Users user = uService.login(username, userPassword);

            if (user != null) {
                // 登录成功

                System.out.println(username + " 登录成功");

                GUIState.getStage().close();


                Stage mainStage = new Stage();
                CommonUtils.setStageStyle(mainStage,true);
                mainStage.setUserData(user);
                GUIState.setStage(mainStage);

                AnchorPane main = new FXMLLoader(getClass().getResource("/fxml/main/main.fxml")).load();

                mainStage.setScene(new Scene(main));
                mainStage.show();

            } else CommonUtils.showAlert("登录失败", "用户名或密码错误", Alert.AlertType.ERROR);
    }
}

执行到 用户登录时 页面和程序的日志:

image.png

Caused by: java.lang.NullPointerException: Cannot invoke "com.course.service.UserService.login(String, String)" because "this.uService" is null at com.course.controller.login.LoginController.userLogin(LoginController.java:45)

注入失败了,实际上。这个错误是SpringBoot 和 JavaFX 没有互联,导致对 Controller 第二次的构造过程中没有出现对uService 的注入行为导致的。

更直白点,SpringBoot 完成了将Controller 存入容器的操作后, JavaFX 又把Controller 构造了一遍 ,而这一次 没有SpringBoot 参与,uService 就没有注入成功


那还有什么方法可以实现呢?


事实上,我们还可以通过一种方式往 Controller 中注入容器对象

方法

这个方法中,标明了 JavaFX 的开发人员实际上是知道这种注入失败的问题的,需要让 Controller 实现一个 Initializable 接口,在重写的方法中实现对 uService 的手动注入,那怎么手动注入呢?


别忘记了,在学习@Autowired 之前,我们是可以通过 applicationContext 来操作容器内的Bean对象的。所以我们现在就是需要一个 BeanUtils,示例如下:


@Component
public class BeanUtils implements ApplicationContextAware {
 protected static ApplicationContext applicationContext;

 @Override
 public void setApplicationContext(ApplicationContext arg0) {
     if (applicationContext == null) {
         applicationContext = arg0;
     }
 }

 public static Object getBean(String name) {
     //name表示其他要注入的注解name名
      return applicationContext.getBean(name);
 }
 /**
  *  拿到ApplicationContext对象实例后就可以手动获取Bean的注入实例对象
  *
  * */
 public static <T> T getBean(Class<T> clazz) {
     return applicationContext.getBean(clazz);
 }

 }

由于这个BeanUtils 是在SpringBoot 中已经完成了初始化的,而且内部的静态方法也可以支持在不同类中调用,所以这个方式是可行的。

完成后的示例代码如下:

private UserService uService;

@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
    uService = BeanUtils.getBean(UserService.class);
}

另外的方法

这种方式仅适用于使用 初级JavaFX开发 的情况。

使用 静态代码块通过 BeanUtils 进行手动注入,所以不多说,上代码:

private static UserService uService;

static {
  uService = BeanUtils.getBean(UserService.class);
}

Maven依赖

需要向Maven中添加如下关于JavaFX的依赖

<dependency>
   <groupId>de.roskenet</groupId>
   <artifactId>springboot-javafx-support</artifactId>
   <version>2.1.6</version>
</dependency>
<dependency>
   <groupId>org.openjfx</groupId>
   <artifactId>javafx-fxml</artifactId>
   <version>22</version>
</dependency>

其他细节

  • 当该项目中出现了其他具有持续监听性质的线程时,即使关闭了最后一个窗口,也不能使程序结束运行,例子就是RabbitMQ 的 消费者监听目标队列的情况
  • 启动项目时,仍然需要在运行的配置中添加本地的JavaFX模块

    详情看我这两篇文章
  1. JavaFX 桌面开发——初级相关补充前言 最近发了一篇关于如何构建 初级的JavaFx项目 的文章,想起来还有一些细节 - 掘金 (juejin.cn)
  2. JavaFX 桌面开发——初级项目构建该文章主要讲述 初级JavaFX的使用以及项目构建方式,讲了如何下载和使用JavaFX - 掘金 (juejin.cn)

总结

  • JavaFX 整合 SpringBoot,可以为之后的桌面开发创造更多可能。
  • JavaFX 整合 SpringBoot,需要考虑启动类的编写,Controller 中容器对象的注入等细节。