Java 8老系统AI Code Review实战:规则扫描+AI分析双层防线,Git合并前先过审

为什么要写这个系列

做Java后端十年,我接触过不少企业的核心系统。

金融、电商、政务------行业不同,但底层的现状惊人地相似:生产系统还在Java 8,框架停在Spring Boot 2.x甚至更早,代码跑了很多年,没人敢轻易动。

去年开始,几乎每个项目都在谈接AI。

但真正动手的时候,团队就卡住了。

不是因为不懂大模型,而是老系统本身接不住。JDK版本不够,Spring AI引不进来;依赖树牵一发动全身,升级一个包怕带崩一片;生产流量压着,不敢拿主流程赌一个AI试点。

更危险的是硬塞。我见过团队在老系统的Service层直接new一个HttpClient调模型API,Prompt拼在业务代码里,超时没配、降级没有。模型响应慢的时候,老系统的订单查询线程池被占满,主流程跟着卡住。还有团队把用户手机号、身份证原样送进Prompt,过了两个月才被安全审计发现。

这种事见多了,我就开始想一个问题:老系统不具备直接接入AI的条件,这是不是大多数企业的常态?

答案是肯定的。而且这不应该成为接不了AI的理由。

核心思路其实就三条:

text 复制代码
老系统少改 ------ 不升级 JDK,不引入 Spring AI 依赖
AI 能力旁路 ------ 独立部署,老系统通过 HTTP 或 MQ 调用
企业边界先行 ------ 脱敏、审计、降级、幂等比模型调用更重要

这个系列就是把这三条线展开成10讲。

从AI Gateway到MCP工具中心,从SQL Agent到RAG知识库,从工单Agent到多Agent研发团队------每一讲都围绕同一个前提:

你的老系统还在跑Java 8,你不能为了AI去赌它的稳定性。

每讲配套一个可运行的Maven Lab,不讲空架构,不写Hello World。你跑得通Demo,看得到边界,拿得到代码。

这就是我做这个系列的原因。


Java 8老系统AI Code Review实战:规则扫描+AI分析双层防线

第4讲我们建立了一张结构化的代码地图:

text 复制代码
扫描源码 → 识别分层 → 生成调用链 → 输出阅读路线

有了这张地图,团队至少知道一个业务动作经过了哪些地方。

但代码每天都在变。

新人会提交代码,老项目会修Bug,业务会加需求,AI也会生成一堆看着像那么回事的实现。

这时候,第4讲的代码阅读地图帮不上了------因为它分析的是项目现状,不是代码变更。

你需要的是在代码合并之前,有人先帮你扫一眼:

text 复制代码
这次改动有没有低级风险?
有没有可能影响老系统稳定性?
有没有漏掉该加的校验?

这就是本讲要解决的问题。

但在开始之前,先说清楚一件事:

AI Code Review 不是替团队做决策,更不是让 AI 自动改代码。

它适合做的是第一道过滤器:把那些容易漏、容易犯、容易在加班时被忽略的低级问题,在合并前提出来。

最终效果

代码目录:

text 复制代码
code/spring-ai-enterprise-lab/labs/chapter05-ai-code-review

运行:

powershell 复制代码
.\compile-and-run.ps1

跑完后能看到:

  • 解析 sample.diff
  • 识别空指针风险、参数校验缺失、库存校验TODO
  • 输出结构化Markdown Review
  • 预留AI分析接口,当前由Stub透传

输入

text 复制代码
Git Diff(本讲用样例 sample.diff 模拟)

输出

text 复制代码
结构化 Markdown Review:
- 风险等级
- 文件
- 行号
- 风险类型
- 原因
- 建议

企业边界

text 复制代码
- 只输出建议,不自动提交代码
- 高风险问题标记需人工确认
- 规则扫描和 AI 分析双层检查
- 输出结构化,方便接入 GitHub / Gitee 评论

为什么不能只靠模型做Review

在讨论怎么实现之前,先想清楚一个问题:为什么Code Review不能直接把Diff丢给模型?

不是因为模型不够好,而是因为Review里有两类完全不同的任务。

第一类:模式匹配型

text 复制代码
空指针风险 → 新增代码里有没有 request.getXxx() 没判空?
SQL 注入风险 → 有没有字符串拼接 SQL?
敏感字段 → 有没有打印手机号、身份证?
事务边界 → 有没有写库操作没包事务?

这些问题有规律,不需要"理解业务"。它们需要一个确定性的规则引擎,而不是一个概率模型。

第二类:语义理解型

text 复制代码
这段代码的设计意图是什么?
这个 catch 掉异常会不会掩盖上游问题?
这个 clientVersion 兼容分支是不是该删了?

这类问题需要上下文、需要解释、需要基于项目事实给出自然建议。这才是模型擅长的事。

所以本讲的核心观点是:

AI Code Review 应该是规则扫描和 AI 分析的双层防线。确定性规则兜底安全问题,AI 负责理解意图和补充上下文。

这一讲先跑通第一层------规则扫描管道。第二层AI分析留好接口,后续接入Spring AI时替换Stub即可。

样例Diff

本讲使用一个经过设计的Diff:

diff 复制代码
@@ -1,6 +1,10 @@
 public String createOrder(OrderRequest request) {
+    String userId = request.getUserId();
+    String productId = request.getProductId();
     if (request.getQuantity() <= 0) {
         throw new IllegalArgumentException("quantity must be positive");
     }
+    // TODO check product stock later
     return orderService.createOrder(userId, productId, request.getQuantity());
 }

这段代码看起来没什么问题,但至少有三个风险点:

第一个问题 :直接调用request.getUserId()request.getProductId()但没有判断request是否为空。如果上游传了一个null,这里直接NPE。

第二个问题 :创建订单前留下了库存校验TODO。// TODO check product stock later说明团队知道这里要加校验,但还没加。

第三个问题 :新增的两个userIdproductId参数没有做非空和格式校验。如果它们是必填字段,说明校验逻辑还在别的地方------或者还没写。

这三个问题,都是规则扫描能识别、也应该识别的。它们不依赖模型,也不依赖对业务的深度理解。

架构设计

本讲链路:

text 复制代码
Git Webhook / Push
        ↓
GitDiffParser
        ↓
┌──────────────────────────┐
│ 第一层:规则扫描            │
│  空指针 / SQL 注入 / 事务   │
│  敏感字段 / 参数校验 / 异常  │
└──────────────────────────┘
        ↓
┌──────────────────────────┐
│ 第二层:AI 分析(预留)      │
│  语义理解 / 上下文补充       │
│  自然建议 / 风险解释         │
└──────────────────────────┘
        ↓
ReviewCommentFormatter
        ↓
Markdown Review

和第1-3讲一样,AI不直接接入老系统流程。老系统的代码变更通过Git Webhook进来,进入独立的Review管道,规则引擎先做确定性扫描,AI再做语义分析,最终输出结构化评论。

这一讲当前只实现了第一层和第二层的接口。Stub模式让管道先跑通,后续替换Stub为真实模型调用时不需要改管道代码。

代码结构

text 复制代码
src/main/java/com/ynzz/lab/chapter05
├── Chapter05Demo.java
├── diff
│   ├── GitDiffParser.java
│   ├── DiffModel.java
│   └── ChangedLine.java
├── review
│   ├── RuleBasedRiskScanner.java
│   ├── AiReviewService.java          ← 接口
│   ├── StubAiReviewService.java      ← Stub 实现
│   ├── CodeReviewService.java
│   ├── ReviewFinding.java
│   ├── ReviewResult.java
│   └── ReviewCommentFormatter.java
└── webhook                             ← 本讲新增
    ├── GitWebhookRequest.java
    └── GitWebhookController.java

子模块(后续扩展):

text 复制代码
git-webhook-adapter/       ← Webhook 签名校验、平台适配
diff-analysis-service/     ← 规则引擎 + AI Review 服务化
review-comment-publisher/  ← 评论发布到 GitHub / Gitee

第一层:规则扫描

本讲Demo实现了三个规则。

规则一:空指针风险

扫描逻辑:

text 复制代码
如果新增代码调用了 request.getXxx()
并且没有出现 request == null 或 request != null
→ 标记空指针风险

规则二:库存校验缺失

扫描逻辑:

text 复制代码
如果新增代码中出现 TODO 且包含 stock 关键词
→ 标记库存校验缺失

这个规则很朴素,但它表达了一个重要原则:Review不仅要关注代码写了什么,还要关注代码应该写但没写什么。

规则三:必填参数无校验

扫描逻辑:

text 复制代码
如果新增代码提取了 userId、productId 等业务参数
但没有出现对应的 null 判断或 StringUtils.isEmpty
→ 标记参数校验缺失

完整的规则维度:

维度 检测内容 风险等级
空指针风险 request.getXxx() 无判空 MEDIUM
参数校验缺失 业务参数无 null/empty 检查 MEDIUM
SQL 注入风险 字符串拼接 SQL 语句 HIGH
事务边界 写库操作无 @Transactional HIGH
敏感字段暴露 日志打印手机号/身份证 HIGH
异常吞没 catch 块只有 log 无 rethrow LOW

当前Demo实现了前三个。后续规则可以通过扩展RuleBasedRiskScanner#scan逐个加入,不影响现有逻辑。

第二层:AI分析(预留)

AiReviewService接口定义:

java 复制代码
public interface AiReviewService {
    /**
     * 基于规则扫描结果,生成 AI 补充分析。
     * 当前 Stub 实现透传原有 finding,不修改。
     */
    List<ReviewFinding> enrich(List<ReviewFinding> findings, DiffModel diff);
}

这个设计有几个好处:

text 复制代码
1. 管道代码(CodeReviewService)不感知是 Stub 还是真实模型
2. 规则扫描的结果是 AI 分析的输入,两层不互相替代
3. 后续接入 Spring AI 只需替换一个实现类
4. 即使 AI 不可用,规则扫描仍然独立工作

这和第4讲"事实层vs表达层"的分工一致:规则负责确定性的事实检测,AI负责非确定性的语义表达。

运行效果

运行Demo后输出:

markdown 复制代码
### AI Review

风险等级:MEDIUM

#### 1. 可能的空指针风险
- 文件:`OrderController.java`
- 位置:第 2 行
- 原因:新增代码直接读取 request 字段,但没有判断 request 是否为空。
- 建议:在进入业务逻辑前增加请求对象校验。

#### 2. 参数校验缺失
- 文件:`OrderController.java`
- 位置:第 3 行
- 原因:新增代码提取了 userId、productId 业务参数,但没有做非空或格式校验。
- 建议:在 Service 入口或 Controller 层增加必填参数校验。

#### 3. 库存校验缺失
- 文件:`OrderController.java`
- 位置:第 7 行
- 原因:新增代码留下库存校验 TODO,创建订单前可能没有真实校验库存。
- 建议:在 Service 层增加库存检查,Controller 不承载业务规则。

企业避坑

第一,AI Review不要自动改代码。

本讲只输出Markdown评论,不生成Patch,不自动提交。AI在Review流程里的角色是"建议者",不是"执行者"。

第二,高风险问题必须进入人工确认。

涉及支付、退款、库存、权限的改动,不能让AI自动判断通过。规则扫描标记HIGH等级后,必须有人在合并前确认。

第三,规则扫描不能丢。

空指针、SQL注入、敏感字段、事务边界这些问题,应该有确定性规则兜底。不能因为有了AI就把规则引擎撤掉。两者是互补关系,不是替代关系。

第四,Review结果必须结构化。

至少要包含:文件、行号、风险等级、风险类型、原因、建议。结构化输出方便接入GitHub/Gitee评论API,也方便后续统计分析。

第五,不要只扫描新增行。

真实Review需要关注上下文行和删除行的语义。比如删掉了一个null判断,只靠"+"行看不出来。

第六,Review要和CI流水线集成。

单独跑一个Review脚本价值有限。更好的嵌入点是:PR创建 → Webhook触发 → Review管道 → 评论回写PR。

从Demo到落地,还差什么

Webhook真实接入GitHub/Gitee :当前Demo用本地sample.diff模拟输入,真实项目需要GitWebhookController接收平台Webhook,校验签名,拉取Diff,再触发Review管道。

规则配置化 :当前规则硬编码在RuleBasedRiskScanner里。企业场景需要把规则抽成配置文件(YAML或JSON),让不同项目、不同团队自定义规则集合和风险等级。

AI分析接入Spring AI :当前StubAiReviewService只做透传。接入真实模型后,AI需要基于Diff上下文解释风险原因、补充业务场景------但这些都是在规则扫描已有结果的前提下做增强。

上下文行分析:当前只扫描新增行。真实diff的上下文行和删除行也需要纳入分析,否则会漏掉"删掉了一个null判断"这类问题。

Review结果回写Git平台:当前只输出到控制台。落地时需要把结构化Review结果以行级评论的形式发布到PR对应位置。

高危变更自动升级:涉及支付、退款、权限模块的变更,应该自动将Review等级提升为HIGH并通知指定Reviewer。

小结

text 复制代码
规则扫描负责确定性检测
AI 分析负责语义理解
两层互补,不互相替代

从第4讲的"读懂老项目"到第5讲的"审查代码变更",这个系列正在从"理解系统"走向"保护系统"。

下一步,第6讲会把企业文档接入RAG知识库------让AI不只是理解代码,还能回答业务问题。

AI Code Review 的目标不是替代 Reviewer,而是让 Reviewer 把精力从找低级错误中解放出来,去关注更重要的设计决策。