前言
到了这一步,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);
}
}
执行到 用户登录时 页面和程序的日志:
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模块 详情看我这两篇文章
- JavaFX 桌面开发——初级相关补充前言 最近发了一篇关于如何构建 初级的JavaFx项目 的文章,想起来还有一些细节 - 掘金 (juejin.cn)
- JavaFX 桌面开发——初级项目构建该文章主要讲述 初级JavaFX的使用以及项目构建方式,讲了如何下载和使用JavaFX - 掘金 (juejin.cn)
总结
- JavaFX 整合 SpringBoot,可以为之后的桌面开发创造更多可能。
- JavaFX 整合 SpringBoot,需要考虑启动类的编写,Controller 中容器对象的注入等细节。