写在前面
你好,我是 Evan。一名正在摸爬滚打的 Java 后端开发者,也是这个专栏的作者。
自从 Copilot 和 Cursor 普及后,我的编码效率确实翻倍了——但 Code Review 的时间也翻倍了。最诡异的是,AI 生成的代码看起来逻辑完整、注释清晰,甚至还有错误处理,可一到运行时,要么抛出奇奇怪怪的异常,要么偷偷把用户密码打到了日志里。“明明写得那么漂亮,为什么一跑就挂?”
这篇文章,我想和你聊聊 AI 生成代码的四种典型“陷阱”,以及我在日常工作中总结的一套人工审阅最佳实践。希望你能在享受 AI 效率红利的同时,守住代码质量的最后一道防线。
一、AI 生成代码的“完美幻觉”
AI 编程工具(Copilot、Cursor)基于海量公开代码训练,能根据上下文预测出“最可能的下一个 token”。这种机制决定了它擅长生成高频的、常规的代码片段,但同时也会盲目复刻训练数据中的错误模式。
一个典型场景:你在注释里写“// 使用 CompletableFuture 并发调用两个接口”,AI 立马生成了一套看似完美的异步编排。但你仔细看,它可能没处理异常,也可能忘记配置线程池——生产环境下直接 OOM。
下面这张图展示了 AI 生成代码从“看起来不错”到“实际运行”的落差:
二、四大陷阱:AI 代码的“温柔一刀”
陷阱1:幻觉代码 —— 调用不存在的库或 API
AI 会信心满满地生成一个看起来非常专业、但实际上根本不存在的函数。
案例:Cursor 生成了一段代码调用StringUtils.reverse(),并注明了来自 Apache Commons Lang3。但实际上 Apache Commons Lang3 并没有reverse()方法(只有StringUtils.reverse()是存在的吗?等一下,我确认一下:实际上 Commons Lang 确实有StringUtils.reverse(String)—— 这是个陷阱!AI 可能正确也可能错误。更典型的幻觉是它写HttpClientUtils.postJson(),但你的项目里根本没有这个类。
Java 后端常见幻觉:
使用虚构的 Spring Boot 注解(如
@RateLimit)调用 JDK 中不存在的工具类(
Thread.sleep()没问题,但TimeUnit.sleep()就是幻觉)推荐了一个不存在的 Maven 依赖坐标
审阅要点:对 AI 引用的任何第三方 API,用 IDE 跳转到定义验证;对于不熟悉的库,先查官方文档。
陷阱2:过时 API —— 活在五年前的版本里
AI 的训练数据混入了不同年代的代码,因此它经常生成已经被标记为@Deprecated的方法。
案例:AI 生成new Date().getYear()—— 自 Java 1.1 起就被废弃。或者用HttpClient4.x 的DefaultHttpClient(早已废弃)。在 Spring 生态中,AI 仍可能生成WebSecurityConfigurerAdapter(Spring Security 5.8 后废弃)。
审阅要点:留意 IDE 的删除线提示;对核心依赖(如 Spring、Guava)的 API,养成查最新文档的习惯。
陷阱3:安全漏洞 —— 看起来无害,实则致命
AI 生成的安全漏洞通常非常隐蔽,它不会写明显的Runtime.exec(userInput),但可能在 SQL 拼接、日志注入、反序列化等方面犯错。
案例一:SQL 注入
// AI 生成的代码 String query = "SELECT * FROM user WHERE name = '" + userName + "'"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query);案例二:日志注入log.info("User: " + userInput)— 攻击者可以伪造日志,绕过监控系统。
案例三:不安全的反序列化ObjectInputStream.readObject()直接读取网络流,可能被利用执行任意代码。
审阅要点:对所有用户输入的数据流向做追踪;使用 SQL 参数化、日志脱敏库(如 logback 的%replace)、安全的序列化方案(如 Jackson 白名单)。
陷阱4:逻辑正确,但非工程化
AI 能写对单个功能,但写不出符合你项目规范的代码——比如没有日志、没有注释、不考虑复用、性能低下。
案例:AI 实现一个缓存,直接在方法内用HashMap,既不设容量上限,也不考虑并发,更不会用 Spring Cache 抽象。
审阅要点:检查是否符合团队编码规范(Google Java Style、阿里巴巴规范);是否可测试(依赖是否可 mock);性能是否符合预期(比如循环内调数据库)。
下面这张图总结了四大陷阱及其典型表现:
三、人工审阅的最佳实践:三层漏斗法
面对 AI 生成的代码,我总结了一套“三层漏斗”审阅流程,既不过度依赖也不全盘否定。
第一层:自动化工具过滤(最快)
在人工看之前,先用工具扫一遍:
IDE 静态检查:IntelliJ 的 Inspections 可以标记很多潜在问题(如
@Deprecated、空指针、资源未关闭)。SpotBugs / SonarQube:扫描安全漏洞和常见 bug。
依赖检查:
mvn dependency:analyze看是否有未声明的依赖。
这一层可以过滤掉 60% 的低级问题。
第二层:差异化审阅(专注高风险区域)
不要逐行读 AI 生成的代码,而是用“差异视角”:
关注 AI主动新增的第三方调用、反射、网络请求、文件读写。
关注数据流:用户输入 → SQL/日志/命令执行。
关注并发:是否操作共享变量、是否加锁正确。
对于 CRUD 样板代码,AI 通常很可靠,不必浪费时间;但对于“非平凡逻辑”,一定要仔细审阅。
第三层:运行与测试(终极验证)
AI 代码必须跑过测试才能合并:
单元测试:补充分支覆盖,尤其异常路径。
集成测试:验证与外部服务(数据库、消息队列)的真实交互。
安全测试:用自动化工具(如 OWASP ZAP)做快速扫描。
小技巧:让 AI 自己生成单元测试——它生成的测试往往能暴露主代码的缺陷。
下图示意了三层漏斗流程:
四、AI 的价值:不该被忽略的生产力红利
说了这么多陷阱,别误会——我依然是 AI 编程工具的忠实用户。关键在于知道什么时候信任,什么时候怀疑。
AI 最适合的场景:
重复性样板代码:DTO、Converter、简单的 CRUD 接口。
代码补全与注释生成:写文档快得飞起。
测试数据构造:快速生成对象工厂。
已知算法的标准实现:比如二分查找、冒泡排序。
AI 最需要警惕的场景:
涉及安全、权限、金钱的计算。
复杂的并发与锁逻辑。
与外部系统集成且文档稀少的边角案例。
对业务规则有严格解释的需求。
五、总结:AI 是副驾驶,你才是机长
回到标题:当 AI 写出“完美代码”时,我审阅什么?审阅的是幻觉、过时、安全漏洞、工程化缺失。审阅的工具是自动扫描 + 差异聚焦 + 运行测试。审阅的宗旨是不盲信、不排斥、用其长、避其短。
AI 不会取代程序员,但会用 AI 的程序员会取代不用 AI 的。关键是,你要始终握着方向盘。