ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

中医体质辨识系统源码解析:Java Swing与MySQL的桌面应用实践

中医体质辨识系统源码解析:Java Swing与MySQL的桌面应用实践 简介这是一份基于Java与HTML实现的中医体质辨识系统源码面向需要学习Java桌面开发、数据库连接及传统医学信息化应用的开发者。项目整体围绕体质评测流程覆盖用户登录、答卷管理、结果查询与可视化展示等模块后端以Java处理业务逻辑与MySQL数据库交互前端用HTML构建简洁操作界面。压缩包共66个文件约17.7MB包含33个Java源文件、12个PNG与3个JPG图片、6个JAR依赖包、4个XML配置以及MP4演示视频、HTML入口页面等。Java源码涵盖界面类、数据访问类与主控逻辑图片资源用于界面美化与体质图解JAR包则提供日期选择、图表绘制和MySQL驱动等能力。目前已有436人学习下载。对于想借鉴完整界面设计、掌握MySQL连接配置及JFreeChart图表展示的读者这份资源提供了可运行的完整工程与必要的第三方库能有效缩短自主搭建时间。1. 中医体质辨识系统Java 桌面应用里的传统医学数字化样本当看到项目里同时出现NewSpiderWebPlot.java和video.html时第一反应是这年头还有人用 Swing 做业务系统。但拆完这份中医体质辨识系统源码包我得说一句公道话Swing 加 MySQL 的组合在校园实训、健康管理 demo 和中小型诊所里依然有真实的落地场景。整个项目 67 个文件33 个 Java 源文件覆盖登录、答卷、结果展示、数据库读写和外部资源加载6 个 JAR 包把 JDBC 驱动、JFreeChart 绘图、BeautyEye 皮肤和日期组件一次性打包进来。适合想快速搭一套带完整业务流程的 Java 桌面应用的开发者也适合需要把体质评估结果可视化的中医信息化项目做参考。这篇文章会从文件结构、核心代码走读、部署调试到二次改造把这份源码的骨架和肉都拆开讲。2. 项目骨架与依赖解析33 个 Java 文件是怎么分层的面对一个陌生源码包第一件事不是读代码而是先看文件树和依赖清单确定代码分成几层、数据往哪流。这一步做对了后面改哪里、查哪里都会快很多。2.1 从 upload.zip 到 src 目录先读懂包结构再动手解压 upload.zip 之后src 下面的包结构分为几块。根包下是 CreateUILogin.java、UserWin.java、Main.java、SearchUI.java、ResultUI.java 等入口和主界面文件FunctionPkg 里放着 CreateSignUI.java、CreateUIL.java 这类可复用的 UI 组件MySQL_im 和 MySQL_im2 各有一个 getInfo.java是两套独立的数据库访问实现Manger 下面是 JTableDemo.java、jdbc.java、Form.java负责管理端逻辑和数据表展示Warning 目录里是 ShowUI.java 和 WarnDialog.java专门处理弹窗告警。从类名能大致推断调用链Main 启动后先拉起 InitWin 或 LogWin登录校验通过后进入 UserWinUserWin 里放问卷入口答题完成走 SearchUI/ResultUI结果需要落库时调 MySQL_im 里的 getInfo。值得注意的还有 InitWin.java 里调用的 BeautyEye 外观库以及 NewSpiderWebPlot.java 这个名字——结果页面要画蜘蛛网图即雷达图。雷达图在 JFreeChart 体系里叫 SpiderWebPlot用来呈现 9 种体质的得分差异非常直观。2.2 六个 JAR 包各自解决什么问题libs 目录下的六个 JAR 包内容如下我按职责和代码对应关系整理成了一张表。JAR 包版本职责对应代码位置mysql-connector-j8.4.0MySQL JDBC 驱动负责数据库连接与 SQL 执行jdbc.java、MySQL_im/getInfo.javajfreechart1.0.10绘制柱状图、饼图、雷达图ResultUI.java、NewSpiderWebPlot.javajcommon1.0.23JFreeChart 的底层公共库提供数据序列接口被 jfreechart 依赖无需直接引用beautyeye_lnf未标注Swing 外观皮肤替换默认的 Metal 风格InitWin.java、LogWin.javadatepicker未标注日期选择组件用于出生日期或答卷日期录入CreateSignUI.java、Form.javayujianbo-java-tools7.8.0自封装工具集含字符串与文件处理FunctionPkg 内多个类这里有两个实际跑下来才会踩到的坑。第一mysql-connector-j 8.4.0 的驱动类名是com.mysql.cj.jdbc.Driver而 5.x 时代的com.mysql.jdbc.Driver在新版本驱动里已经被移除很多老博客还在用旧的类名直接 ClassNotFoundException。第二JFreeChart 1.0.10 对 JCommon 的版本要求是 1.0.23 配套版本对不上会在启动时抛 NoClassDefFoundError而且报错信息只给一长串类路径不直接告诉你是哪个依赖缺了排查起来比较绕。2.3 jdbc.java 与 MySQL 8.4 驱动的连接写法Manger 包下的 jdbc.java 是整套系统的数据库入口代码很精简连接参数直接硬编码在类里。我把它拆开逐行看package Manger; import java.sql.*; public class jdbc { // 8.x 驱动必须用 com.mysql.cj.jdbc.Driver旧类名已失效 private static final String DRIVER com.mysql.cj.jdbc.Driver; // URL 里三个参数编码、时区、SSL 开关缺一个都会报错 private static final String URL jdbc:mysql://localhost:3306/tcm_db ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/ShanghaiuseSSLfalse; private static final String USER root; private static final String PASSWORD 123456; private static Connection conn; // 静态块在类加载时执行一次完成驱动注册和连接建立 static { try { Class.forName(DRIVER); conn DriverManager.getConnection(URL, USER, PASSWORD); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } // 所有查询统一走这个静态方法调用方不需要感知连接细节 public static ResultSet executeQuery(String sql) throws SQLException { Statement stmt conn.createStatement(); return stmt.executeQuery(sql); } }代码里有三个参数值得细说。useUnicodetrue配合characterEncodingutf8是写死中文字段不乱码的底线配置但注意 utf8 在 MySQL 8 里已经被 utf8mb4 全面替代如果表字段里有生僻字或 emoji这个连接串要改成 characterEncodingutf8mb4。serverTimezoneAsia/Shanghai解决 MySQL 8.x 对 JVM 时区不确定性的校验不加会在建立连接时直接抛异常。useSSLfalse是给本地调试用的生产环境如果数据库和客户端跨网段传输则应该去掉这个参数并配置证书。这个类把连接生命周期完全交给静态引用单线程演示够用后续做到并发就要换成连接池这点到第 5 章展开。3. 核心流程走读登录、答卷、出报告一条线从登录窗口到最终的结果报告中间隔着校验、问卷计分、图表绘制和资源加载四道流程。逐个拆开看这四部分基本对应了项目里最重要的几个 Java 文件。3.1 LogWin 登录窗口到 UserWin 主界面的跳转实现登录窗口是整个系统的入口。根据源码的命名约定LogWin 持有登录表单登录成功后销毁自身并打开 UserWin典型的 Swing 窗口流转写法如下// LogWin.java 登录成功后的跳转逻辑 private void login() { String uid uidField.getText().trim(); // JPasswordField 要用 getPassword() 拿 char[]不要用 getText() String pwd new String(pwdField.getPassword()); if (uid.isEmpty() || pwd.isEmpty()) { WarnDialog.show(用户名和密码不能为空); return; } boolean ok MySQL_im.getInfo.verifyUser(uid, pwd); if (ok) { this.dispose(); // 关闭当前登录窗口避免资源占用 new UserWin(uid).setVisible(true); // 把用户身份传进主窗口 } else { WarnDialog.show(用户名或密码错误); } }这段代码有一个值得揣摩的设计UserWin 构造函数接收 uid而不是登录后再查一次库。这样既省了一次无谓的数据库 IO也把当前会话身份给到了后续所有界面——答题记录、结果保存、历史查询都可以直接拿 uid 关联。另外注意密码控件用 JPasswordField 时取值走getPassword()返回 char[]转成 String 用完后最好立刻覆盖置空避免密码长期残留在堆内存里。虽然桌面应用的安全基线不高但这个习惯在代码评审时会被盯上。3.2 体质辨识的题目组织与 9 种体质判定逻辑问卷计分是这套系统的核心规则。中医体质判定标准把人群分为 9 种基本体质分别是平和质、气虚质、阳虚质、阴虚质、痰湿质、湿热质、血瘀质、气郁质、特禀质。每类体质对应一组问题答案按 1 到 5 分记录最后汇总每个维度的原始得分再换算成百分制的转化分。// 9 个体质维度按问卷固定顺序编号 public static int[] calcScore(int[][] answers) { // answers 是 9 行 N 列的二维数组每行对应一种体质的全部题目答案 int[] total new int[9]; for (int dim 0; dim 9; dim) { for (int j 0; j answers[dim].length; j) { total[dim] answers[dim][j]; // 每题 1~5 分直接累加 } // 转化分 (原始分 - 题目数) / (题目数 * 4) * 100 int count answers[dim].length; total[dim] (int) Math.round( (total[dim] - count) / (double) (count * 4) * 100); } return total; }转化分的公式是体质辨识量表的通用做法原始分先减掉题目数得到的是相对最低分的偏移量再除以最大可能增量最后乘 100 映射到百分制。判定阈值通常分三档——转化分大于等于 40 判定为该体质倾向明确30 到 39 为有倾向小于 30 基本不成立。ResultUI 拿到这 9 个分数后会挑出超过阈值的维度给出文字结论和改善建议。需要注意的是实际项目的题目数量和分组方式以数据库里的问卷表为准上面代码用的是每组固定题目数的常见约定。3.3 ResultUI 中 JFreeChart 的雷达图与柱状图绘图NewSpiderWebPlot.java 这个文件名叫得相当直白结果页的核心图表就是 SpiderWebPlot即雷达图。JFreeChart 1.0.10 里这个类位于 org.jfree.chart.plot 包能够按维度均匀分布角度把 9 个体质得分画成一个闭合多边形。接入 ResultUI 的典型写法// ResultUI.java 中雷达图的构建过程 DefaultCategoryDataset dataset new DefaultCategoryDataset(); String[] dims {平和,气虚,阳虚,阴虚,痰湿,湿热,血瘀,气郁,特禀}; int[] scores calcScore(answerData); // answerData 来自问卷录入 for (int i 0; i 9; i) { // addValue(值, 行键, 列键) 的行键决定雷达图顶点标签 dataset.addValue(scores[i], dims[i], 体质得分); } SpiderWebPlot plot new SpiderWebPlot(dataset); plot.setStartAngle(90D); // 第一维从 12 点钟方向开始顺时针排布 plot.setInteriorGap(0.25); // 控制图形边界与绘图区的间距越大图越小 plot.setMaxValue(100); // 坐标上限固定为 100对应百分制转化分 JFreeChart chart new JFreeChart( 中医体质辨识结果, TextTitle.DEFAULT_FONT, plot, true); ChartPanel panel new ChartPanel(chart); panel.setPreferredSize(new Dimension(520, 380)); resultPanel.add(panel, BorderLayout.CENTER);setStartAngle(90D)让第一个维度平和质从正上方开始而不是默认的零度夹角这样图形看起来更对称。setMaxValue(100)把雷达图的半径上限固定成百分制确保不同用户之间的图谱可以直接比较不会因为最高分差异导致图的比例尺漂移。需要特别留意的是JFreeChart 默认标题字体是 Dialog在中文 Windows 环境下会把中文渲染成方块应该在构造 Title 时显式指定new Font(宋体, Font.PLAIN, 14)。这个问题放到 Linux 服务器上用无界面模式导出图片时会更明显。3.4 OpenWeb 与 video.html资源文件在桌面程序里的挂载方式项目根目录下的 video.html 和中医体质.mp4 是配套资源OpenWeb.java 负责把它们拉到系统浏览器里打开。Java 6 以后的 Desktop API 可以很干净地完成这件事import java.awt.Desktop; import java.io.File; public class OpenWeb { public static void open(String filePath) { try { File f new File(filePath); if (f.exists()) { // browse() 接受 URI直接用文件转 URI 即可 Desktop.getDesktop().browse(f.toURI()); } else { // 兜底无 Desktop 环境时退回 Runtime 调用 Runtime.getRuntime().exec(cmd /c start filePath); } } catch (Exception e) { e.printStackTrace(); } } }为什么桌面程序里要嵌一个 HTML 页面常见做法是把使用说明、答题引导或体质科普视频做成 HTML 放在项目目录比在 Swing 里拼 JLabel 和 JTextArea 排版要高效得多改样式也不用重新编译。视频部分用 HTML5 的video controls src中医体质.mp4标签加载本地文件比在 JFrame 里嵌 JFXPanel 或 VLCJ 轻量得多后者要为不同平台带一堆本地库部署成本立刻上去。4. 本地部署与联调驱动、时区、字体三大坑把源码包在本地跑起来通常会卡在三个地方数据库没初始化、MySQL 8.4 连接参数不对、Swing 外观在特定系统上字体渲染异常。逐个解决整个系统就能转起来。4.1 初始化 MySQL 表结构与测试数据jdbc.java 里写死了库名 tcm_db所以第一件事是在本地 MySQL 建库建表。由于源码包没有附带 SQL 脚本我一般会先建最小可用的两张表用户表和答卷结果表。CREATE DATABASE IF NOT EXISTS tcm_db DEFAULT CHARACTER SET utf8mb4; USE tcm_db; -- 用户表登录、角色、注册时间 CREATE TABLE IF NOT EXISTS t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 密码演示项目用明文, role CHAR(1) DEFAULT 0 COMMENT 0-普通用户 1-管理员, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; -- 答卷结果表每次测评一行存 9 个体质转化分 CREATE TABLE IF NOT EXISTS t_result ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, record_date DATE NOT NULL, score_peace INT DEFAULT 0, -- 平和质转化分 score_qi INT DEFAULT 0, -- 气虚质 score_yang INT DEFAULT 0, -- 阳虚质 score_yin INT DEFAULT 0, -- 阴虚质 score_phlegm INT DEFAULT 0, -- 痰湿质 score_wet INT DEFAULT 0, -- 湿热质 score_blood INT DEFAULT 0, -- 血瘀质 score_qi_stagn INT DEFAULT 0, -- 气郁质 score_allergy INT DEFAULT 0, -- 特禀质 FOREIGN KEY (user_id) REFERENCES t_user(id) ) ENGINEInnoDB; INSERT INTO t_user(username, password) VALUES (admin, admin123);建表有两个细节。第一字符集指定 utf8mb4 而不是 utf8因为 utf8mb4 才能完整覆盖中文字符集的扩展区MySQL 8 的默认字符集就是 utf8mb4老项目迁移过来要显式声明。第二9 个体质字段用 INT 存转化分够了阈值判定和绘图都只需要整数不需要 DECIMAL。表结构建好后用SHOW CREATE TABLE t_result;核对一遍字符集和索引避免后面写入中文报 Incorrect string value。4.2 驱动类名、SSL 与 serverTimezone 参数核对MySQL 8.4 的连接约束和旧版本差异很大几乎每个从 5.x 升级上来的项目都会在这里栽跟头。我把常见报错和对应处理整理成了一张表。报错信息原因处理方式ClassNotFoundException: com.mysql.jdbc.Driver用了 5.x 时代的旧驱动类名改成com.mysql.cj.jdbc.DriverAccess denied for user rootlocalhost账号密码错误或未授权检查 MySQL 账号授权GRANT ALL ON tcm_db.* TO rootlocalhostThe server time zone value ... is unrecognized服务端时区与 JVM 不一致URL 追加serverTimezoneAsia/ShanghaiSSL connection error8.x 默认启用 SSL 校验本地调试加useSSLfalsePublic Key Retrieval is not allowedcaching_sha2_password 认证需要取公钥加allowPublicKeyRetrievaltrueCould not create connection to database server驱动版本与服务端版本差距过大统一用 8.4.0 驱动至少 8.0.20表格里那个The server time zone value ... is unrecognized是 8.0 之后最容易出现的。MySQL 驱动会在握手阶段读取服务端时区如果值是系统默认的 CST 且字符集不是驱动认识的格式直接抛异常。两条路可以解决一条是在 JDBC URL 里显式写serverTimezoneAsia/Shanghai另一条是在 MySQL 里执行SET GLOBAL time_zone 08:00。前者更干净因为跟着代码走换部署机器不用重新配置数据库。4.3 BeautyEye 外观库在不同系统下的表现差异BeautyEye 是把 Swing 默认外观替换成扁平化风格的开源库但初始化时机和字体设置决定了它在不同系统上的表现。常见的初始化代码是这样的// Main.java 的入口方法 public static void main(String[] args) { try { // 必须在任何 JFrame 实例化之前设置外观 org.jb2011.lnf.beautyeye.BeautyEyeLNFHelper.launchBeautyEyeLNF(); UIManager.put(RootPane.setupButtonVisible, false); } catch (Exception e) { // 兜底外观初始化失败时退回系统外观 try { UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName()); } catch (Exception ex) { ex.printStackTrace(); } } new InitWin().setVisible(true); }执行顺序非常关键。launchBeautyEyeLNF()必须在任何窗口对象创建之前执行否则控件加载时已经使用了默认的 Metal 皮肤中途再切换只会导致部分控件风格错位。UIManager.put(RootPane.setupButtonVisible, false)是 BeautyEye 的特色开关用来隐藏窗口右上角额外生成的控制按钮。在 Windows 10 上这个皮肤默认使用微软雅黑到了精简版 Linux 系统中文字体缺失界面上所有中文会变成方块。我一般会在 main 方法里加上一行全局字体设置UIManager.put(defaultFont, new Font(Microsoft YaHei, Font.PLAIN, 12));这样即使系统 locale 不是中文只要装了雅黑或文泉驿字体界面就能正常渲染。如果连字体都没有那就得把字体文件随项目分发或者在启动脚本里加-Dfile.encodingutf-8配合区域设置否则问题定位会花很长时间。5. 二次开发的三处提速点把原系统跑通之后如果要拿它做二次开发或课程设计改造我建议优先改这三处改动量小、收益明显、踩坑预期也更可控。5.1 用连接池替换裸 JDBC 语句jdbc.java 里静态 Connection 是所有查询共用的答题并发一上来Statement 会互相干扰结果集也会串。替换成 HikariCP 是这个场景下成本最低的方案项目自带的 MySQL 8.4 驱动正好满足 HikariCP 的版本要求HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/tcm_db ?useSSLfalseserverTimezoneAsia/Shanghai); config.setUsername(root); config.setPassword(123456); config.setMaximumPoolSize(10); HikariDataSource ds new HikariDataSource(config); // 从连接池拿连接的耗时通常小于 1ms远低于新建连接的 10-20ms Connection conn ds.getConnection();改成连接池之后原来的static Connection全局引用可以删掉每次操作数据库从池子里取连接、用毕归还。唯一要注意的是HikariCP 所在 jar 包要手动放进 libs 目录并在项目里配置好 IDE 的依赖路径否则运行时直接 NoClassDefFoundError。5.2 JFreeChart 图表直接导出 PNG 文件结果页面的雷达图目前只在窗口里渲染用户想留存报告还得手动截图。JFreeChart 提供了直接的导出接口可以绕过窗口把图表写成 PNG 文件// 把 chart 对象直接输出为文件不依赖窗口存在 File out new File(report_ userId .png); ChartUtils.saveChartAsPNG(out, chart, 800, 600);saveChartAsPNG的第四个参数是图片宽度第五个是高度按 800x600 输出正好适合打印 A4 报告。配合 3.3 节的 chart 对象这个功能可以在用户答完题后自动生成一张带体质的图谱存到本机或者上传到数据库字段里做成历史记录。如果想在 Linux 服务器上无头模式跑这个导出功能启动参数要加-Djava.awt.headlesstrue否则无显示环境下会抛 HeadlessException。5.3 把 HTML 资源嵌入 jar 包避免路径丢失OpenWeb.java 目前用文件路径找 video.html源码目录下跑没问题一旦打成可执行 jar相对路径就失效了——jar 包内部不是普通文件系统。常见的做法是把 HTML 和视频作为资源打包运行时先释放到临时目录再交给系统浏览器// 从 classpath 读取资源并释放到临时文件 InputStream in OpenWeb.class.getResourceAsStream(/web/video.html); File tmp File.createTempFile(tcm_report, .html); Files.copy(in, tmp.toPath(), StandardCopyOption.REPLACE_EXISTING); Desktop.getDesktop().browse(tmp.toURI());这里需要注意getResourceAsStream的路径要以斜杠开头并且对应文件要放在 src/main/resources/web 目录下。临时文件用完后不会自动清理建议在关闭程序时检查 tmp 目录并按文件名前缀清理避免长期运行后临时目录堆积垃圾文件。改造完这一处发布时只需要交付一个 jar 和一份数据库初始化脚本项目从源码工程变成真正可分发的软件包。本文还有配套的精品资源点击获取
返回列表