1. 两个看似灵异的开发Bug实录
上周连续遇到两个匪夷所思的技术问题:一个SpringBoot项目的依赖Jar包在服务器上神秘消失,另一个Excel文件竟然会"记忆"被删除的数据。作为有十年排查诡异问题经验的老码农,我把完整排查过程和解决方案记录下来,这些经验你在官方文档里绝对找不到。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Jar包消失之谜
2.1 现象描述
生产环境突然报NoClassDefFoundError,检查发现原本存在于libs/目录下的hutool-pinyin.jar不翼而飞。更诡异的是,这个文件前一天还正常存在,且服务器没有人工操作记录。
2.2 排查过程
- 磁盘空间检查:
df -h显示磁盘未满,inode也正常 - 系统日志分析:通过
journalctl -u servicename发现服务每天凌晨3点自动重启 - 启动脚本检查:发现使用了SpringBoot的
loader.path参数:bash复制
java -Dloader.path=libs/ -jar application.jar - 文件系统监控:用
auditd跟踪发现jar包在服务重启时被删除
2.3 根本原因
SpringBoot的LaunchedURLClassLoader在热加载时会清理旧的classpath资源。当使用-Dloader.path指定外部目录时,该目录下的jar包会被视为临时资源,在应用重启时被自动清理。
2.4 解决方案
- 推荐方案:将第三方jar包打进应用主jar(maven配置
<scope>compile</scope>) - 临时方案:改用
-cp参数指定classpath:bash复制java -cp "application.jar:libs/*" com.MainClass
踩坑提示:使用
nssm将jar包注册为Windows服务时,同样要注意classpath的配置方式,错误的路径设置会导致依赖丢失。
3. Excel的"记忆"功能
3.1 问题重现
使用EasyExcel导出数据时,发现导出的xlsx文件中包含已从数据库删除的记录。这些"幽灵数据"在Excel界面不可见,但用Python的openpyxl读取时却能获取到。
3.2 技术分析
- Excel文件结构:现代xlsx实质是ZIP压缩包,包含多个XML组件
- 数据存储机制:
/xl/worksheets/sheet1.xml存储当前显示数据/xl/sharedStrings.xml可能保留历史字符串/xl/workbook.xml包含缓存索引
3.3 问题定位
FastExcel等库在流式导出时会复用SharedStrings表项,当数据被"删除"时:
- 单元格引用被移除(sheet1.xml)
- 但对应的字符串仍保留在sharedStrings.xml
- 某些解析库会读取全部字符串表
3.4 彻底解决方案
- 强制重建文件:
java复制// EasyExcel示例 ExcelWriterBuilder builder = EasyExcel.write(file) .registerWriteHandler(new CleanSheetHandler()); // 自定义清理策略 class CleanSheetHandler implements SheetWriteHandler { @Override public void afterSheetCreate(...) { sheet.getWorkbook().getSharedStringSource().clear(); } } - Python处理方案:
python复制from openpyxl import load_workbook wb = load_workbook('problem.xlsx', keep_links=False) wb.save('clean.xlsx')
4. 开发环境中的Jar包管理
4.1 IDEA常见陷阱
- 未引用jar包清理:在
File → Project Structure → Modules中,未使用的依赖会被标记为灰色,建议定期清理 - 依赖冲突检测:使用
mvn dependency:tree -Dverbose查看冲突 - 本地jar引用:通过
system作用域引入时,必须确保路径固定
4.2 打包最佳实践
xml复制<!-- 保证第三方jar包打进最终产物 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
5. Excel数据处理避坑指南
5.1 内存优化技巧
- 流式读取:使用EasyExcel的
ReadListener避免OOMjava复制EasyExcel.read(file, new AnalysisEventListener() { @Override public void invoke(Object data, AnalysisContext context) { // 单行处理逻辑 } }).sheet().doRead(); - 缓存禁用:对于大数据量导出,设置
inMemory=false
5.2 格式处理难点
- 换行符问题:在CSV中转义
\n为\\n - 数字格式化:明确指定单元格格式,避免科学计数法
java复制WriteCellStyle style = new WriteCellStyle(); style.setDataFormat((short)4); // 文本格式
6. 诡异Bug排查方法论
-
时间锚点法:精确记录问题首次出现的时间点,检查该时段的:
- 系统日志(
/var/log) - 部署记录(Jenkins或Git记录)
- 监控指标(CPU/内存波动)
- 系统日志(
-
环境对比法:建立与生产环境完全一致的沙箱,使用
docker commit保存现场 -
二进制取证:对于文件损坏类问题,使用
hexdump和diff工具对比正常/异常文件
我在处理这两个问题时,发现开发文档很少提及这些边缘情况。特别是SpringBoot的classloader行为,直到查看源码才明白其清理机制。建议遇到类似问题时,不要局限于错误信息本身,要关注系统层面的交互影响
