news 2026/10/8 13:55:25

医药管理系统源码拆包:从class反编译到MySQL落库的完整链路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
医药管理系统源码拆包:从class反编译到MySQL落库的完整链路

简介:这是一套基于Java Web技术栈的医药管理系统源码,面向计算机专业学生、课程设计开发者及需要练手SSM/JSP项目的初学者,可帮助快速搭建药品进销存管理场景。系统覆盖药品添加与查看、高级查询、库存管理、类别维护与统计、购买药品、销售管理、进货需求管理及系统管理等后台功能,业务链路较为完整。压缩包共162个文件,约9.34MB,以31个jsp页面、30个java源文件、30个class编译文件、26个jar依赖包及12个xml配置为主,另含sql建库脚本、properties配置与少量图片资源,结构清晰便于二次开发。开发环境为JDK1.8搭配MySQL数据库,导入后即可对照源码理解分层设计与数据库交互逻辑。目前已有2058人学习下载,适合作为课程设计、毕业设计或Java Web入门实战的参考项目。

1. 医药管理系统源码拆包:从 class 文件到 MySQL 落库的完整链路

拿到一个只有.class没有.java的医药管理系统源码包,第一反应往往是「这玩意儿还能改吗」。我拆过不少这种包,结论是:能跑、能改、能二次开发,但前提是你得先搞清楚它的分层结构和数据库依赖。这个包的核心是一套后台管理功能,覆盖药品添加、库存查看、类别统计、购买销售、进货需求、系统管理这几块,技术栈是 JDK 1.8 + MySQL,典型的 Java Web 三层架构。适合两类人:一是做 Java 课程设计需要现成业务逻辑参考的,二是想拿一套完整 CRUD + 统计报表练手 MySQL 建表和 DAO 封装的。它不是什么高并发架构,但业务闭环是完整的,药品从入库到销售到补货需求这条线能跑通,这在教学和快速原型场景里比很多半成品强。

2. 反编译与工程还原:把 class 还原成可维护的 Java 工程

2.1 为什么必须先反编译再谈二次开发

源码包里只有SellAction.class、MedicineAction.class、UserDao.class这类编译产物,没有.java源文件。这意味着你直接改业务逻辑是改不动的——class 文件是 JVM 字节码,不是给人读的。常见做法是用 JD-GUI 或 CFR 做反编译,把字节码还原成近似源码。注意,反编译出来的代码变量名可能是arg0、var1这种,方法体里的泛型和 lambda 也可能变形,但业务逻辑和 SQL 调用链是完整的,足够你理解它怎么运转。

我一般会先反编译BaseAction.class和SupperDao.class,因为这两个是父类,其他 Action 和 DAO 都继承它们。看懂父类里的连接获取、事务提交、异常回滚逻辑,后面每个子类的行为就都能对上号。

2.2 反编译操作步骤与工具参数

# 用 CFR 反编译整个包,输出到 src 目录 java -jar cfr.jar medicine-system.zip --outputdir ./src --caseinsensitivefs true # 如果只想看某个类的逻辑,单独反编译 java -jar cfr.jar SellAction.class --outputdir ./single

CFR 的--caseinsensitivefs true在 Windows 上必须加,否则大小写敏感的文件系统判断会报错。反编译完成后,你会得到一堆.java文件,但目录结构是扁平的,需要按包名手动归位。我通常按action、dao、form、entity四层重建目录,这样后面导入 IDE 才不会一团乱。

2.3 工程结构还原与依赖补齐

反编译出来的代码不能直接编译,缺依赖。从类名判断,MedicineForm和Medicine是表单和实体,UserDao、SupperDao是数据访问层,*Action是控制层。这套命名很像早期 Struts2 或 Servlet + JDBC 的写法。你需要补的依赖通常包括:

依赖项用途常见版本
servlet-apiAction 层的 HTTP 处理3.1 或 4.0
mysql-connector-java数据库驱动5.1.x 或 8.0.x
commons-dbutils 或手写 JDBCDAO 层查询封装1.7
jstl页面标签1.2

补齐后先别急着跑,把SupperDao里的数据库连接参数找出来,通常是硬编码的DriverManager.getConnection或者一个简易连接池。这一步的坑在于:反编译后的字符串常量可能被混淆,URL 里的&会变成&,需要手动改回来。

2.4 验证反编译结果是否可用

最直接的验证方式是写一个 main 方法调用UserDao的查询方法,看能不能连上 MySQL 并返回结果。如果报ClassNotFoundException,说明驱动没引入;如果报No suitable driver,检查 JDBC URL 前缀是不是jdbc:mysql://。这一步过了,说明反编译和依赖补齐是成功的,可以进入建库阶段。

3. MySQL 建库与 DAO 层适配:让药品、类别、销售三张表跑起来

3.1 从实体类反推表结构

Medicine.class和MedicineForm.class是药品相关的实体,字段通常包括药品 ID、名称、类别 ID、单价、库存量、生产日期、保质期。CategoryAction对应类别表,字段是类别 ID 和类别名称。SellAction涉及销售记录,需要销售 ID、药品 ID、销售数量、销售时间、操作员。反推出来的建表语句大致如下:

CREATE TABLE medicine ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, category_id INT, price DECIMAL(10,2), stock INT DEFAULT 0, produce_date DATE, expire_date DATE, FOREIGN KEY (category_id) REFERENCES category(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE sell_record ( id INT PRIMARY KEY AUTO_INCREMENT, medicine_id INT, quantity INT, sell_time DATETIME, operator VARCHAR(50), FOREIGN KEY (medicine_id) REFERENCES medicine(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

字符集用utf8mb4而不是utf8,因为药品名称里可能出现生僻字或特殊符号,utf8在三字节以上字符会截断。外键约束建议加上,虽然有些教学项目为了省事不加,但加上之后你的类别统计查询才不会出现孤儿记录。

3.2 DAO 层查询适配与参数绑定

反编译出来的MedicineDao里大概率是拼接 SQL 字符串的写法,这种写法在 JDK 1.8 环境下能跑,但有两个问题:一是注入风险,二是参数类型不匹配时直接抛异常。我一般会把它改成PreparedStatement预编译形式:

// 改造前:字符串拼接,容易出错 String sql = "SELECT * FROM medicine WHERE name LIKE '%" + keyword + "%'"; // 改造后:预编译,参数绑定 String sql = "SELECT * FROM medicine WHERE name LIKE ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, "%" + keyword + "%"); ResultSet rs = ps.executeQuery();

setString的索引从 1 开始,不是 0,这个血泪经验每次带新人都要强调一遍。另外LIKE的通配符%要拼在参数值里,不能写在 SQL 里,否则预编译失效。

3.3 类别统计与库存查询的 SQL 实现

类别统计是这套系统的核心功能之一,本质是GROUP BY加聚合:

SELECT c.name AS category_name, COUNT(m.id) AS medicine_count, SUM(m.stock) AS total_stock FROM category c LEFT JOIN medicine m ON c.id = m.category_id GROUP BY c.id, c.name;

用LEFT JOIN而不是INNER JOIN,这样没有药品的类别也会显示,数量为 0。库存查询则是带条件的SELECT,通常按库存量倒序或按类别筛选。高级查询功能一般支持多条件组合,比如名称模糊 + 类别精确 + 价格区间,这种动态 SQL 在 DAO 层要用StringBuilder拼 WHERE 子句,但参数仍然走预编译绑定。

3.4 购买与销售的事务处理

购买药品涉及两个操作:插入销售记录、扣减库存。这两个操作必须在一个事务里,否则会出现卖了药但库存没减的情况。反编译代码里如果用的是conn.setAutoCommit(false)加commit()/rollback(),那逻辑是对的;如果没看到事务控制,需要手动补上:

Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 插入销售记录 sellDao.insert(conn, sellRecord); // 扣减库存 medicineDao.reduceStock(conn, medicineId, quantity); conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); conn.close(); }

注意setAutoCommit(true)要在close之前恢复,否则连接回到池里会带着未提交状态,下一个请求就翻车了。

4. 避坑与常见问题排查:反编译包最容易卡住的五个地方

4.1 反编译后中文乱码

现象:反编译出来的 Java 文件里,药品名称、类别名称等中文字符串显示为乱码或问号。原因:class 文件里的字符串常量是 UTF-8 编码,但反编译工具默认用系统字符集读取,Windows 中文环境是 GBK,导致解码错误。解决:CFR 加--encoding utf-8参数重新反编译;JD-GUI 在设置里把字符集改成 UTF-8。如果已经反编译完,用 IDE 的「重新加载文件编码」功能批量转码。

4.2 数据库连接报时区错误

现象:启动后连接 MySQL 报The server time zone value '?D1ú±ê×?ê±??' is unrecognized。原因:MySQL 8.0 驱动要求显式指定时区,而反编译出来的 JDBC URL 里没有serverTimezone参数。解决:在 URL 后面加?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8。注意&在 XML 配置文件里要写成&,在 Java 代码里直接写&。

4.3 Action 层 404 但类已编译

现象:访问药品列表页面返回 404,但控制台没有报错,class 文件也确实在WEB-INF/classes下。原因:反编译后的 Action 类可能丢失了注解或web.xml里的 servlet 映射没配。早期项目用web.xml配置 Action 映射,如果反编译时没还原web.xml,请求就找不到入口。解决:检查WEB-INF/web.xml是否存在,如果缺失,根据 Action 类名手动补<servlet>和<servlet-mapping>。或者改用注解方式重新注册。

4.4 库存扣减出现负数

现象:购买药品后库存变成负数,或者并发购买时库存扣减不正确。原因:扣减库存的 SQL 是UPDATE medicine SET stock = stock - ? WHERE id = ?,没有加AND stock >= ?条件,也没有在事务里加行锁。解决:改成UPDATE medicine SET stock = stock - ? WHERE id = ? AND stock >= ?,根据返回的受影响行数判断是否扣减成功。并发场景下在SELECT时加FOR UPDATE行锁。

4.5 类别统计数字对不上

现象:类别统计页面显示的药品数量和实际列表数量不一致。原因:统计 SQL 用了COUNT(*)但 JOIN 产生了笛卡尔积,或者LEFT JOIN后COUNT统计的是主表行数而不是关联表行数。解决:用COUNT(m.id)而不是COUNT(*),确保统计的是关联表的实际记录数。如果有多表 JOIN,先用子查询聚合再关联。

5. 进阶用法:把反编译工程改造成可调试的 Maven 项目

反编译出来的工程最大的问题是没法断点调试——你看到的代码和实际运行的字节码有差异,行号对不上。我的做法是把它重构成 Maven 项目,补上pom.xml,然后用 IDEA 的「Attach to Process」或者直接以 Debug 模式启动 Tomcat。这样你改完 DAO 层的 SQL,能直接在PreparedStatement那行打断点,看参数绑定的值对不对。

具体步骤:先建一个标准 Maven Web 项目骨架,把反编译的src按包名放进src/main/java,web.xml和 JSP 放进src/main/webapp。pom.xml里加上 servlet-api、mysql-connector、jstl 三个依赖,scope 该 provided 的写 provided。然后配 Tomcat 运行,启动时如果报ClassNotFoundException,看缺哪个类就往 pom 里加对应依赖。

验证改造是否成功,我一般会走一遍完整业务流:添加类别 → 添加药品 → 查看库存 → 购买药品 → 查看销售记录 → 查看类别统计。每一步都打断点看 SQL 执行结果。这个流程走通,说明工程还原是完整的,后面你想加新功能、改统计维度、接前端框架,都有了一个可调试的基座。

从那以后我每次拿到只有 class 的源码包,都强制先反编译、再建库、再跑通一条完整业务流,三步缺一不可。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 13:55:11

PS5通用玩法手册:账号安全、SSD扩展与常见故障排查指南

2020年底我入手第一台PS5的时候&#xff0c;兴奋劲还没过&#xff0c;就被一系列问题缠住了&#xff1a;游戏装到哪里、账号要不要开两步验证、待机模式开着到底费不费电、为什么下载速度忽快忽慢、备份存档到底要怎么搞。折腾了几个月&#xff0c;我把这套经验整理成了一个自己…

作者头像 李华
网站建设 2026/10/8 13:53:26

金融风控实战:从特征工程到反欺诈模型与贷中预警全解析

干这行这么多年&#xff0c;我发现自己做得最多的项目&#xff0c;反而不是那些听起来高大上的推荐系统、用户画像&#xff0c;而是金融风控。大数据和数据科学在这个领域的应用&#xff0c;可以说是把“脏活累活”都干了一遍&#xff1a;从海量交易里揪出欺诈&#xff0c;在用…

作者头像 李华
网站建设 2026/10/8 13:50:23

dlib 68点人脸关键点检测:从环境配置到实时应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 13:49:44

OSPF仿真工程实战:OPNET区域划分、负载均衡与收敛分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 13:49:17

工业电源路径保护:TPS259483AYWPR与MKV42F128VLH16协同设计实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 13:49:00

Superpowers:面向IDE的AI技能中枢与本地模型调度框架

1. 项目概述&#xff1a;Superpowers 不是超能力&#xff0c;而是开发者工具链的“认知增强层” “Superpowers”这个词最近在开发者社区里频繁刷屏&#xff0c;但别被字面意思带偏——它不是什么玄学插件&#xff0c;也不是科幻电影里的特效开关。我第一次在 GitHub Trending…

作者头像 李华