调试的本质是推理,不是追踪。
背景:调试为什么值得单独说
大多数开发者调试的默认方式是:加断点,单步执行,反复尝试。这种方式在简单场景下有效,但面对复杂问题时:
- 并发问题难以复现
- 生产环境无法断点
- 跨模块调用链太长
- N+1 查询、事务失效这类隐蔽 Bug,肉眼很难发现
AI 辅助调试的核心思路是:先推理,再验证。不是一步步跟踪代码,而是把报错、代码、环境等所有上下文交给 AI,让 AI 先分析所有线索,给出根因假设,再针对性验证。
这个思路下,AI 能同时处理的信息量远超人类,推理速度也更快。尤其适合:
- 复杂调用链的问题
- 偶发性并发问题
- 需要结合配置、环境才能判断的根因
本文面向 Java 后端场景,提供可直接使用的调试 Prompt 模板和 Claude Code 实战技巧。
📖 目录
- 🤔 AI 调试 vs 传统调试:范式转变
- 📝 四大调试 Prompt 模板
- 🛠️ Claude Code 调试实战技巧
- 💼 五大高频 Java 后端调试场景
- 🚀 进阶:AI 生成复现测试与防御性代码
- 📋 调试 Prompt 检查清单
🤔 AI 调试 vs 传统调试:范式转变
传统调试流程
发现问题 → 看报错 → 猜原因 → 加断点 → 重现 → 确认 → 修复
问题在于:每一步都依赖人工经验,且难以并行。猜原因要靠积累,加断点要能找到关键位置,复杂问题往往要反复试错很久。
AI 调试流程
发现问题 → 收集上下文 → AI 推理可能原因 → 针对性验证 → 修复
AI 能同时处理:完整 Stack trace + 相关代码 + 环境描述 + 历史上下文,并行推理多种可能性,人类只需验证最可能的假设。
什么时候用 AI 调试
| 问题类型 | 适合 AI 调试 | 原因 |
|---|---|---|
| 异常/报错分析 | ✅ 非常适合 | Stack trace 信息完整,AI 推理准确率高 |
| 逻辑错误(无报错但行为不对) | ✅ 适合 | 需要结合期望/实际行为对比分析 |
| 并发问题 | ✅ 适合 | AI 能识别典型并发反模式 |
| 性能问题 | ⚠️ 需要数据 | AI 能分析代码,但需要 GC/log/慢查询数据辅助 |
| 第三方库问题 | ⚠️ 有限 | 需要具体报错+代码,库内部逻辑 AI 只能推测 |
📝 四大调试 Prompt 模板
模板一:异常分析(最常用)
请帮我分析这个 Java 异常:
【异常类型】
{异常类名}
【完整 Stack Trace】
{粘贴完整的 stack trace}
【相关代码】
{粘贴出错的代码片段}
【环境信息】
- Java 版本:{版本}
- 框架:{Spring Boot 版本等}
- 数据库:{如果有}
请分析:
1. 这个异常的根本原因是什么?
2. 是业务层面的问题还是代码 bug?
3. 最可能的触发场景是什么?
4. 如何修复?请给出具体代码修改。
5. 如何在代码中加入防御性检查,避免再次发生?
效果好的关键:Stack trace 必须完整,不要截断;代码片段要包含出错的方法上下文,不是只贴一行。
模板二:逻辑错误分析
我遇到了一个逻辑问题,请帮我分析:
【期望行为】
{描述期望的行为}
【实际行为】
{描述实际发生的行为}
【相关代码】
{粘贴完整的方法/类代码}
【已尝试的方法】
{描述你试过的调试方法}
请分析:
1. 代码中哪里出现了逻辑错误?
2. 为什么这里会出错?(思维链)
3. 如何修复?
4. 有没有类似的模式需要检查整个代码库?
模板三:并发问题分析
请帮我分析这个并发问题:
【现象描述】
{描述在并发场景下出现的问题}
【相关代码】
{粘贴涉及多线程的代码}
请分析(重点检查以下典型并发反模式):
1. 共享可变变量是否没有同步?
2. 集合类是否使用了非线程安全的实现(ArrayList/HashMap)?
3. 事务边界是否不当导致数据不一致?
4. 是否存在锁粒度过粗或死锁风险?
如果发现并发问题:
- 最可能的触发条件是什么?
- 如何修复?(优先推荐:ThreadLocal > 原子类 > synchronized > ReentrantLock)
- 如何写测试用例来复现这个问题?
模板四:性能问题分析
请帮我分析这个性能问题:
【性能数据】
- 问题方法:{方法名}
- 单次耗时:{xxx ms}
- 期望耗时:{xxx ms}
- 调用频率:{QPS 或调用次数}
【慢查询/关键日志】
{paste 慢查询 SQL 或关键日志}
【相关代码】
{粘贴代码}
【JVM 信息】(如果有)
{paste GC 日志或 JStack 线程 dump 关键部分}
请分析:
1. 性能瓶颈最可能在哪个环节?(数据库?CPU?锁竞争?内存?)
2. 导致慢的根本原因是什么?(N+1?全表扫描?锁等待?)
3. 如何优化?(按优先级列出方案)
4. 优化后预期提升多少?
5. 如何写性能测试验证优化效果?
🛠️ Claude Code 调试实战技巧
技巧一:先推理,再验证
不要一上来就让 AI 修改代码。先让 AI 给出分析,确认后再修复。
# 第一步:只让 AI 推理分析
claude --print "分析这段代码为什么在并发场景下会出问题,
只输出分析结果和根因假设,不需要修改代码。"
# 第二步:确认后让 AI 修复
claude --print "基于之前的分析,帮我修复这个并发问题"
为什么这样做:
- 避免 AI 在没完全理解问题前就开始乱改
- 让你先审核 AI 的推理过程是否正确
- 调试效率更高——修错方向比不修更浪费时间
技巧二:提供足够的上下文
调试质量完全取决于输入质量。Claude Code 调试必需的上下文:
1. Stack trace:完整,不要省略 Caused by 部分
2. 相关代码:出错的方法 + 调用它的地方,不只是一行
3. 环境描述:Java 版本、Spring Boot 版本、数据库版本
4. 复现步骤:如果知道的话(触发条件、频率)
# ✅ 好的做法:提供完整上下文
claude --print "帮我分析这个 Spring Boot 启动失败的问题:
【完整 Stack Trace】
$(cat /tmp/startup-error.log)
【出错的配置类】
$(cat /tmp/FailedBeanConfig.java)
环境:Java 17 + Spring Boot 3.2.0
请分析根因和修复方案。"
# ❌ 差的做法:上下文不足
claude --print "Spring Boot 启动失败了怎么办"
技巧三:用 –resume 保持调试上下文
复杂调试往往需要多轮对话,不要每个问题都开新会话:
# 启动调试会话
claude
# 多轮对话后,关闭
# 第二天继续
claude --resume <session-id>
调试上下文(输入的代码、对话历史)都被保留,AI 能继续深入分析。
技巧四:对比验证修复正确性
修复后不要直接提交,让 AI 对比验证:
请对比【修复前】和【修复后】的代码:
1. 原问题是否已修复?
2. 是否引入了新问题?
3. 代码风格是否一致?
【修复前】
{paste original code}
【修复后】
{paste fixed code}
技巧五:让 AI 生成 Debug 日志辅助定位
当 AI 分析后仍不确定时,让它帮你加日志:
基于对这段代码的分析,在关键路径上添加 DEBUG 日志
来辅助定位问题:
- 方法入口/出口
- 分支判断点
- 循环内的关键变量
- 外部调用(数据库、HTTP、Redis)
要求:
- 每个方法最多 3-5 条日志
- 使用 Slf4j:log.debug("methodName | var={}", var)
- 不影响原有逻辑和性能
💼 五大高频 Java 后端调试场景
场景一:Spring Boot 启动失败(Bean 初始化异常)
典型报错:UnsatisfiedDependencyException、BeanCreationException
Prompt:
Spring Boot 应用启动失败,请帮我分析:
【完整报错】
{粘贴启动失败的所有异常 stack trace}
【出错的配置类】
{粘贴报错的配置类或启动类}
请分析:
1. 哪个 Bean 初始化失败了?
2. 根本原因是什么?(循环依赖?配置缺失?类型不匹配?)
3. 如何修复?
4. 如何避免此类问题?(Spring Boot 启动检查建议)
重点检查:
@Autowired的 Bean 不存在- 循环依赖(Bean A 依赖 B,B 依赖 A)
@ConfigurationProperties绑定失败- 第三方服务连接失败
场景二:MyBatis SQL 执行异常
典型报错:TypeException、BindingException、SQL 语法错误
Prompt:
MyBatis 执行 SQL 时报错,请帮我分析:
【报错信息】
{粘贴 MyBatis 相关异常}
【出错的 SQL】
{粘贴出问题的 SQL}
【Mapper 代码】
{粘贴 Mapper XML 或注解}
【实体类相关部分】
{粘贴出错的字段映射部分}
请分析:
1. SQL/映射的哪里有问题?
2. 是参数绑定问题?字段映射问题?还是 SQL 语法问题?
3. 如何修复?
重点检查:
#{}vs${}的使用(参数绑定 vs 字符串拼接)resultMap的字段映射是否正确- 枚举类型是否配置了 TypeHandler
- NPE 是否由 null 参数导致
场景三:@Transactional 事务不生效
这是 Java 后端最隐蔽的 Bug 之一。
Prompt:
我的 @Transactional 事务没有生效,请帮我分析:
【问题描述】
{描述预期的和实际的事务行为}
【相关代码】
{粘贴包含 @Transactional 的完整方法}
请重点检查以下常见的事务失效原因:
1. 方法内部自调用(this.method())—— 代理对象问题
2. 非 public 方法—— Spring 事务基于代理,public 以外不代理
3. 异常被 catch 吞掉了—— 事务基于异常回滚,吞异常则不回滚
4. 事务传播行为不当—— REQUIRED vs REQUIRES_NEW 等
5. 方法返回后异步执行了数据库操作—— 异步在线程池执行,不在事务内
如果确认是内部自调用问题:
- 给出通过 ApplicationContext 获取自身代理的最佳方案
- 给出通过 AOP 切面解耦的方案
- 说明为什么 this.method() 不能被代理
根因速查表:
| 原因 | 表现 | 修复 |
|---|---|---|
| 内部自调用 | 方法内调用同类另一个@Transactional 方法 | 通过代理对象调用 |
| 异常被 catch | 方法内 try-catch 吞了异常 | rethrow 或 Transactional.rollbackFor |
| 非 public 方法 | 编译器报错或日志无声失败 | 改为 public |
| rollbackFor 未指定 | 非 RuntimeException 不回滚 | 指定 rollbackFor |
| 传播行为错误 | REQUIRES_NEW 在同一事务中不生效 | 确认传播行为配置 |
场景四:并发场景下的数据竞争
Prompt:
请帮我分析这个并发数据竞争问题:
【现象描述】
{描述在并发场景下出现的数据不一致/重复提交等问题}
【涉及共享资源的代码】
{粘贴涉及多线程读写的代码}
【已有的并发控制】
{如果有同步措施,粘贴}
请检查:
1. 哪些变量/资源是共享的?
2. 是否有足够的同步措施?
3. 是否存在典型反模式:
- DateFormat/SimpleDateFormat 非线程安全
- HashMap/ArrayList 在多线程中使用
- Long/Double 等"原子"类型在并发下的复合操作
- 数据库连接在多线程间共享
修复方案:
- 按优先级推荐方案(ThreadLocal > Atomic > synchronized > ReentrantLock)
- 给出具体代码修改
场景五:线上 OOM(内存泄漏)排查
这类问题无法在本地复现,必须靠日志和 dump 分析。
Prompt:
请帮我分析这个内存问题:
【GC 日志】
{paste GC log}
【JStack 线程 dump 关键部分】
{paste 线程dump 中 BLOCKED/WAITING/TIMED_WAITING 的线程}
【堆内存使用情况】
{paste jstat -gc 等输出}
【问题描述】
{描述 OOM 发生的时机、频率、进程重启情况}
请分析:
1. 内存使用趋势是什么?(持续增长?周期性变化?)
2. 是否有内存泄漏的迹象?
3. 最可能的泄漏点在哪里?
4. 如何定位具体的泄漏对象?(给出 jmap/ MAT 分析的建议)
5. 短期应急方案和长期修复方案分别是什么?
🚀 进阶:AI 生成复现测试与防御性代码
进阶一:让 AI 生成能复现 Bug 的测试用例
分析完根因后,让 AI 生成 JUnit 测试来稳定复现问题:
基于以上根因分析,请生成一个 JUnit 5 测试用例来复现这个 Bug。
要求:
1. 使用 @ParameterizedTest 测试多组边界数据
2. 包含清晰的 @DisplayName 说明测试场景
3. 测试必须能稳定复现问题(不是有时成功有时失败)
4. 断言要精确(不只是 assertNotNull,要检查具体的值和状态)
格式:
```java
@DisplayName("场景描述")
@ParameterizedTest
@CsvSource({
"输入1, 期望1",
"输入2, 期望2"
})
void testName(String input, String expected) {
// given
// when
// then
}
### 进阶二:AI 自动生成防御性代码
让 AI 在修复后加上防御性措施:
基于这个 bug 的根因,请在修复代码的同时加入以下防御措施:
- 入口参数校验(if null/invalid throw IllegalArgumentException)
- 添加 assert 断言关键假设
- 在异常路径添加日志(ERROR 级别,记录关键变量)
- 如果是并发问题,添加 ThreadMXBean 检测(在线程池执行前检查)
要求:
- 修改尽可能少,不破坏原有逻辑
- 防御性代码和业务逻辑分离(不要混在一起)
- 添加 TODO 注释说明防御措施的原因 ```
进阶三:让 AI 帮你写 Chaos 测试
针对这个并发问题,请帮我写一个 chaos 测试用例:
- 使用 ExecutorService 模拟并发场景
- 多次执行(100+ 次)来提高复现概率
- 在 CI 中可以稳定失败(发现问题就 FAIL)
使用 JUnit 5 + AssertJ:
```java
@Test
@DisplayName("并发场景:Xxx 问题")
void testXxxUnderConcurrency() throws InterruptedException {
// given: 准备测试数据
// when: 多线程并发执行
// then: 断言最终状态正确
}
---
## 📋 调试 Prompt 检查清单
每次用 AI 调试前,对照这个清单检查上下文是否充足:
□ 1. Stack trace 完整(包含所有 Caused by) □ 2. 相关代码完整(不只是报错的一行,是方法上下文) □ 3. 环境信息明确(Java 版本、框架版本、数据库版本) □ 4. 复现步骤已知(如何触发这个问题) □ 5. 已尝试的方法已知(避免 AI 重复试错) □ 6. 期望行为 vs 实际行为描述清楚(逻辑错误时必须) □ 7. 选择了正确的 Prompt 模板 □ 8. 先推理后修复(不急着让 AI 直接改代码) □ 9. 修复后有对比验证(确认没引入新问题) □ 10. 记录了根因到团队知识库(下次同类问题更快定位) ```
总结:AI 调试的最佳实践
- 上下文第一:调试质量 = 输入质量。Stack trace 必须完整,代码必须完整。
- 先推理后修复:让 AI 先给出分析,确认推理正确后再让 AI 修改。
- 选对模板:异常/逻辑/并发/性能,用对模板事半功倍。
- 用 –resume 保持上下文:复杂问题不要开多个会话。
- 对比验证修复:AI 修完后,用对比 prompt 确认没有引入新问题。
- 生成复现测试:修复 + 写测试,确保同类问题不再出现。
- 记录根因:把调试结论写入团队知识库,下次同类问题 5 分钟内解决。