资讯动态

SpringBoot3整合JavaFx与MyBatisPlus桌面端开发实战

发布时间:2026/10/18 17:34:46 来源:尧图企业网站定制
简介这是一套面向Java后端与桌面应用开发者的整合实战项目基于SpringBoot3搭建基础框架结合JavaFx构建跨平台富客户端界面并以MyBatisPlus作为数据持久层帮助开发者解决传统桌面应用配置繁琐、数据操作重复的问题适合具备一定Spring与Java基础、希望拓展全栈桌面开发能力的中高级学习者。压缩包共79个文件约110.76MB包含16个java源码、19个class编译文件、12个fxml界面布局、7个xml与2个yml配置、1个sql脚本以及png、jpg、ico等图标资源和mp4演示视频覆盖从界面设计到数据访问的完整结构。目前已有2422人学习下载。项目展示了JavaFx场景启动器与SpringBoot容器的整合方式、MyBatisPlus通用CRUD与分页插件的配置思路并附带可运行jar包便于读者对照源码理解前后端协作流程快速复用到自己的桌面管理类项目中。1. 桌面端跑 SpringBoot3 整合 JavaFx MyBatisPlus到底图什么很多做 Java 后端的兄弟第一次听到「SpringBoot3 整合 JavaFx MyBatisPlus」这个组合第一反应是桌面端不是已经凉了吗为什么还要把 Spring 全家桶塞进一个客户端程序里我当初也这么想直到接手一个需要本地读写数据库、又要复用现有后端 Service 层逻辑的内部工具需求。纯 Swing 写界面太痛苦纯 Web 又要额外部署服务端最后选了 JavaFx 做壳、SpringBoot3 做容器、MyBatisPlus 做持久层一套代码同时管界面和数据访问。这个方案解决的核心问题是让桌面程序也能享受 Spring 的依赖注入、事务管理和 MyBatisPlus 的 CRUD 便利不用手写一堆单例和 JDBC 模板。适合谁适合手里已经有 SpringBoot 后端经验、需要快速交付一个带本地数据库的桌面工具、又不想学 Electron 或 Qt 的 Java 开发者。下面把我踩过的整合路径完整拆开。2. 环境搭建与依赖版本对齐别让 JavaFx 和 SpringBoot3 打架2.1 为什么 JavaFx 在 SpringBoot3 里容易翻车JavaFx 从 JDK11 开始就不再随 JDK 捆绑需要单独引入模块。而 SpringBoot3 要求 JDK17 起步两者对模块路径的处理方式不同。常见做法是用 Maven 的 javafx-maven-plugin 来启动而不是直接 java -jar否则会报「JavaFX runtime components are missing」。我一般会把 JavaFx 版本锁在 21.0.2和 JDK17 搭配最稳再高的版本在部分 Windows 机器上会出现渲染线程和 Spring 上下文初始化顺序冲突。另一个坑是 SpringBoot3 默认用 Jakarta EE 命名空间MyBatisPlus 必须用 3.5.5 以上版本才支持 jakarta.persistence 注解。如果你从旧项目复制依赖很容易带进来 javax 的包启动时直接 ClassNotFound。2.2 最小可跑的 pom 依赖清单!-- 父工程锁定 SpringBoot3 版本 -- parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.5/version /parent properties java.version17/java.version javafx.version21.0.2/javafx.version /properties dependencies !-- SpringBoot 基础容器不引入 web 避免端口占用 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter/artifactId /dependency !-- MyBatisPlus 必须 3.5.5 才兼容 SpringBoot3 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version3.5.5/version /dependency !-- H2 本地文件数据库桌面端首选 -- dependency groupIdcom.h2database/groupId artifactIdh2/artifactId version2.2.224/version /dependency !-- JavaFx 控件与 FXML -- dependency groupIdorg.openjfx/groupId artifactIdjavafx-controls/artifactId version${javafx.version}/version /dependency dependency groupIdorg.openjfx/groupId artifactIdjavafx-fxml/artifactId version${javafx.version}/version /dependency /dependencies逻辑说明这里刻意不引 spring-boot-starter-web因为桌面端不需要内嵌 Tomcat引了反而会让 Spring 启动时去绑定端口拖慢启动速度。MyBatisPlus 的 starter 名字带 spring-boot3 后缀这是官方为 Jakarta 命名空间单独发的包别用错。参数说明javafx.version 建议和本机 JDK 的 JavaFx 模块版本一致不一致时用 javafx-maven-plugin 启动会报模块解析失败。H2 用文件模式连接串写 jdbc:h2:./data/appdb数据就落在程序运行目录下方便打包后随身携带。2.3 启动类怎么写才不丢 Spring 上下文SpringBootApplication public class DesktopLauncher extends Application { // 把 Spring 上下文暴露成静态变量供 Controller 层取 Bean private static ConfigurableApplicationContext context; Override public void init() { // JavaFx 的 init 阶段先启动 Spring保证 Bean 就绪 context SpringApplication.run(DesktopLauncher.class); } Override public void start(Stage stage) throws IOException { // 从 Spring 容器里拿 FXML 加载器而不是 new 一个 FXMLLoader loader context.getBean(FXMLLoader.class); Scene scene new Scene(loader.load()); stage.setScene(scene); stage.show(); } Override public void stop() { // 窗口关闭时优雅停掉 Spring释放数据库连接池 context.close(); Platform.exit(); } public static void main(String[] args) { launch(args); } }逻辑说明JavaFx 的 Application 生命周期分 init、start、stop 三段。把 SpringApplication.run 放在 init 里能保证 start 阶段加载 FXML 时所有 Service、Mapper 都已实例化。如果放在 start 里FXML 的 Controller 通过 Autowired 注入 Bean 会拿到 null这是最常见的翻车点。参数说明FXMLLoader 要注册成 Bean 并设置 setControllerFactory(context::getBean)否则 FXML 里写的 fx:controller 会走默认反射构造绕开 Spring 依赖注入。这一步不做Controller 里的 Autowired 全部失效。3. MyBatisPlus 在桌面端的持久层配置与 CRUD 落地3.1 数据源和 Mapper 扫描的桌面端写法# application.yml spring: datasource: url: jdbc:h2:./data/appdb;AUTO_SERVERTRUE driver-class-name: org.h2.Driver username: sa password: sql: init: mode: always schema-locations: classpath:db/schema.sql mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml configuration: map-underscore-to-camel-case: true global-config: db-config: id-type: auto逻辑说明AUTO_SERVERTRUE 允许多个进程同时连同一个 H2 文件调试时开两个窗口不会锁死。schema-locations 指向建表脚本SpringBoot3 启动时自动执行桌面端首次运行就能建好表结构省去手动初始化。参数说明map-underscore-to-camel-case 打开后数据库的 user_name 会自动映射到实体类的 userName。id-type 设为 auto 表示用数据库自增主键桌面端单机场景够用如果要做多端同步再换 ASSIGN_ID 雪花算法。3.2 实体与 Mapper 的最小闭环// 实体类对应 H2 里的 user 表 Data TableName(t_user) public class User { TableId(type IdType.AUTO) private Long id; private String userName; private Integer age; } // Mapper 接口继承 MyBatisPlus 的 BaseMapper 即拥有 CRUD Mapper public interface UserMapper extends BaseMapperUser { // 复杂查询才需要自己写简单 CRUD 不用动 ListUser selectByAgeGt(Param(age) Integer age); }!-- resources/mapper/UserMapper.xml -- select idselectByAgeGt resultTypecom.example.demo.entity.User SELECT * FROM t_user WHERE age gt; #{age} ORDER BY id DESC /select逻辑说明BaseMapper 已经提供了 insert、deleteById、updateById、selectList 等方法桌面端 80% 的增删改查不用写 SQL。只有多表关联或复杂条件才在 XML 里手写。注意 XML 里的 要转义这是 MyBatis 解析 XML 的硬性要求直接写 会报错。参数说明TableName 指定表名不写则默认用类名小写。TableId 的 type 要和 yml 里的 id-type 一致否则插入时主键生成策略会冲突。3.3 在 JavaFx Controller 里调用 ServiceComponent RequiredArgsConstructor public class UserController implements Initializable { private final UserService userService; FXML private TableViewUser userTable; FXML private TableColumnUser, String nameCol; Override public void initialize(URL url, ResourceBundle rb) { // 绑定列到实体属性JavaFx 的 PropertyValueFactory 按反射取值 nameCol.setCellValueFactory(new PropertyValueFactory(userName)); refreshTable(); } private void refreshTable() { // 从数据库查全量桌面端数据量小直接塞进 ObservableList userTable.setItems(FXCollections.observableArrayList(userService.list())); } FXML private void onAddClick() { User u new User(); u.setUserName(test); u.setAge(20); userService.save(u); refreshTable(); } }逻辑说明Controller 实现 Initializable 接口initialize 在 FXML 加载完成后自动调用此时 Spring 已经注入了 userService。用 RequiredArgsConstructor 做构造器注入比字段注入更利于测试。refreshTable 每次操作后重新查库桌面端数据量通常在几千条以内性能完全够。参数说明PropertyValueFactory 的字符串必须和实体 getter 去掉 get 后首字母小写一致写错不会报错但列显示空白这是新手最容易卡住的地方。如果实体字段是 Integer 而列泛型写 String运行时会抛 ClassCastException。4. 避坑与常见问题排查整合路上最容易翻车的五件事4.1 启动报「JavaFX runtime components are missing」现象用 java -jar 启动打包后的程序控制台直接抛这个异常窗口不出现。 原因JavaFx 的模块没有被打进 fat jar或者启动时没走模块路径。 解决用 javafx-maven-plugin 的 javafx:run 启动调试打包时用 shade 插件并把 JavaFx 的 classifier 设为 win/linux/mac 对应平台或者干脆用 jlink 生成带运行时的镜像。4.2 Controller 里 Autowired 注入的 Service 为 null现象FXML 加载成功界面能显示但一点按钮就空指针。 原因FXMLLoader 默认用反射 new Controller没走 Spring 容器。 解决把 FXMLLoader 注册成 Bean并调用 setControllerFactory(context::getBean)让 Spring 接管 Controller 的实例化。4.3 MyBatisPlus 分页插件在桌面端不生效现象调用 page 方法返回全部数据limit 没拼进 SQL。 原因SpringBoot3 下分页插件需要手动注册 MybatisPlusInterceptor旧版本的 PaginationInterceptor 已废弃。 解决写一个配置类注入 MybatisPlusInterceptor 并添加 PaginationInnerInterceptor(DbType.H2)桌面端用 H2 就指定 H2 方言。4.4 H2 数据库文件被锁第二次启动报「Database may be already in use」现象程序异常退出后重新打开连不上数据库。 原因H2 的文件锁没释放或者上次进程没完全退出。 解决连接串加 AUTO_SERVERTRUE 和 DB_CLOSE_ON_EXITFALSE并在 stop 方法里显式关闭 context让连接池先释放再退出 JVM。4.5 打包后 FXML 和 Mapper XML 找不到现象开发环境正常打成 jar 后报「Location is not set」或「Invalid bound statement」。 原因Maven 默认不把 resources 下的非 class 文件打进 jar或者路径用了绝对路径。 解决pom 里配置 resources 插件包含 **/.fxml 和 **/.xml代码里用 getClass().getResourceAsStream 加载不要用 new File。5. 进阶技巧让桌面端启动更快、打包更小、升级更省心5.1 用懒加载把启动时间压到 2 秒内SpringBoot3 默认会在启动时扫描所有 Bean桌面端如果 Service 很多启动会明显变慢。我一般给非首屏需要的 Bean 加 Lazy 注解让它们在第一次被调用时才初始化。比如报表导出模块的 Service用户不点导出就不加载。实测能把冷启动从 4 秒压到 2 秒左右。另外把 spring.main.lazy-initialization 设为 true 可以全局懒加载但要注意 Mapper 的懒加载可能导致第一次查询变慢需要权衡。5.2 用 jlink 裁剪运行时安装包从 180MB 降到 60MB传统 fat jar 会把整个 JDK 的模块都带上体积很大。用 jlink 只保留 java.base、java.sql、javafx.controls 等实际用到的模块生成的运行时镜像能小三分之二。命令大致是 jlink --add-modules java.base,java.sql,javafx.controls --output runtime然后用 jpackage 把 runtime 和 jar 一起打成 exe 或 dmg。注意 jpackage 在 Windows 上需要 WiX ToolsetLinux 上需要 fakeroot这些是打包环境的依赖不是代码问题。5.3 数据库版本升级用 Flyway 管起来桌面端程序发出去之后用户本地已经有数据了直接改 schema.sql 会导致老用户升级后表结构对不上。常见做法是引入 Flyway把每次表结构变更写成 V1__init.sql、V2__add_column.sql 这样的迁移脚本程序启动时自动比对版本号并执行增量。MyBatisPlus 负责 CRUDFlyway 负责 DDL两者分工明确。配置上只要把 spring.flyway.enabled 设为 truelocations 指向 classpath:db/migration 即可。5.4 一个验证整合是否成功的检查清单检查项预期结果失败时看哪里Spring 上下文启动控制台无异常Bean 数量正常启动类 init 方法FXML 加载窗口显示控件可交互FXMLLoader 的 controllerFactoryMapper 注入Controller 里 Service 非 nullMapperScan 路径数据库读写新增后表格刷新出数据H2 连接串和 schema.sql打包运行双击 exe 能启动jlink 模块列表和 resources 配置这张表是我每次新项目必过的流程任何一项挂了就先修那一项不要跳步。整合类项目最怕的就是多个问题叠在一起逐个隔离才能快速定位。5.5 我自己的习惯我现在做这类桌面端整合第一步永远是先写一个只有 SpringBoot 加一个 RestController 的空壳确认能启动第二步加 JavaFx 空窗口第三步才加 MyBatisPlus 和数据库。每加一层就跑一次出问题立刻知道是哪一层的锅。这个习惯帮我省了无数个通宵排查的夜晚。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑