为什么要写这个系列
做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解释项目
前三讲,我们一直站在"AI如何接入老系统"的视角看问题:
text
第 1 讲:老系统不升级,通过 AI Gateway 旁路接入 AI
第 2 讲:老系统 API 不能裸奔,要包装成受控工具
第 3 讲:AI 生成 SQL 只能作为候选,必须经过安全引擎
这三个场景有一个共同点:AI要和老系统发生关系。
但这里有一个更底层的问题:如果团队自己都没有读懂老系统的结构,AI又怎么可能稳定地接入它?
很多Java 8老项目已经跑了七八年甚至十几年。写代码的人离职了,文档只剩一半,Controller里可能夹着业务逻辑,Service里可能有历史兼容分支,Mapper里还保留着旧字段。新人不敢改,老员工也只敢动自己熟悉的那几块。
这时候直接问AI:
text
帮我解释这个类
帮我改这个方法
帮我接一个新能力
很容易得到一段"看起来很合理"的回答。问题是,它可能只理解了局部代码,却没有理解这段代码在整个项目里的位置。
所以第4讲先不讲RAG,也不讲工作流。这一讲要补上一个基础能力:
在让AI解释老项目之前,先把入口、分层、调用链、异常分支和数据边界这些结构化事实整理出来。
后面无论是做代码评审、知识库问答、工单分析,还是更复杂的研发Agent,都离不开这一步。
本讲要解决什么
这一讲的目标不是让AI直接改代码。目标是先生成一张"老项目阅读地图":
text
给一个 Java 8 老项目
↓
扫描源码文件
↓
识别 Controller / Service / Mapper
↓
找到入口方法和调用链
↓
标出业务规则、兼容分支、异常处理、数据落库边界
↓
生成一条新人能看懂的阅读路线
核心观点:
text
结构化事实在前,AI 解释在后。
不要让模型一上来就凭感觉猜项目结构。先让代码扫描器告诉它:项目里有哪些类、请求从哪里进、业务规则在哪一层、数据最终写到哪里。然后AI再基于这些事实去解释、总结、问答,可信度才会高很多。
本讲Demo
代码目录:
text
code/spring-ai-enterprise-lab/labs/chapter04-code-reading
运行:
powershell
.\compile-and-run.ps1
当前Demo不依赖真实模型,也不需要API Key。它先跑通"事实层":
text
legacy-codebase-sample
↓
JavaProjectScanner
↓
LayerDetector
↓
CallChainAnalyzer
↓
ReadingPathGenerator
这一步是AI读代码之前必须准备的上下文。当前Demo负责生成模型可以依赖的结构化事实;后续接入真实模型时,模型再负责把这些事实转成更自然的解释、摘要和问答。
换句话说:这一讲跑通事实层,后续再让模型基于事实做表达层。
样例老项目
本讲内置了一个极简但带典型老系统特征的Java 8老订单项目:
text
legacy-codebase-sample
└── src/main/java/com/ynzz/legacy/order
├── OrderController.java
├── OrderService.java
└── OrderMapper.java
入口层:历史客户端版本
OrderController带上了历史客户端版本:
java
public String createOrder(String userId, String productId, int quantity, int clientVersion) {
return orderService.createOrder(userId, productId, quantity, clientVersion);
}
业务层:三个老系统常见点
OrderService#createOrder里有:
java
if (quantity <= 0) {
throw new IllegalArgumentException("quantity must be positive");
}
if (clientVersion == 1) {
normalizedProductId = "LEGACY-" + productId;
}
catch (RuntimeException ex) {
return "CREATE_ORDER_FAILED";
}
至少有三件事值得读:
text
quantity <= 0 业务校验
clientVersion == 1 历史兼容分支
catch RuntimeException 异常兜底
持久化层:历史字段痕迹
OrderMapper#insert里保留了历史字段痕迹:
java
// legacy_channel: 2018 mobile client only, old SQL mappings still reference this field.
// String legacyChannel = "APP_V1";
System.out.println("[Mapper] INSERT INTO order_tab(...)");
老项目最麻烦的地方,往往不是主流程看不懂,而是:
text
为什么这里有个兼容分支?
这个 catch 是业务兜底还是临时补丁?
这个旧字段还能不能删?
Mapper 里有没有隐藏的数据副作用?
如果AI没有先看到这些结构事实,就很容易给出过于乐观的改造建议。
假设你只把OrderService#createOrder丢给模型。模型大概率会告诉你:
text
这个方法负责创建订单。
它校验 quantity。
它生成 orderId。
它调用 Mapper 插入订单。
这段解释没错,但还不够。因为它没有回答:
text
clientVersion 从哪里来?
clientVersion == 1 是不是还在线上使用?
CREATE_ORDER_FAILED 返回给谁?
Controller 有没有继续包装这个返回值?
Mapper 里的 legacy_channel 是否仍影响旧 SQL?
这就是"局部解释"和"项目理解"的差别。局部解释只看一个方法,项目理解要看入口、调用链、分层职责和数据边界。
所以第4讲要先生成结构化阅读路线,再让AI基于这条路线解释项目。
第一步:扫描项目文件
JavaProjectScanner做的事情很朴素:
text
找到项目目录下的 .java 文件
读取类名、路径和源码文本
组装成 JavaProject
它不理解业务,也不调用模型。但它给后续分析提供了事实来源。
你不能默认模型已经知道项目结构。你要先告诉它:
text
项目里有哪些类
每个类在哪个路径
源码文件之间可能有什么关系
这一步越清楚,后面的AI总结越不容易跑偏。
第二步:识别代码分层
LayerDetector用类名规则识别分层:
text
OrderController -> controller
OrderService -> service
OrderMapper -> mapper
不同层的代码,阅读目的不一样:
- Controller:重点看入口、参数、返回。
- Service:重点看业务规则、异常分支、状态变化。
- Mapper:重点看数据边界、字段映射、持久化副作用。
如果AI不知道这些层次,它可能会把Mapper里的历史字段当成业务规则,也可能忽略Service里的兼容分支。
第三步:生成调用链
CallChainAnalyzer从入口方法开始,生成一条最小调用链:
text
OrderController#createOrder
↓
OrderService#createOrder
↓
OrderMapper#insert
调用链的价值不在于它有多长,而在于它回答了一个企业开发最常见的问题:
text
这个业务动作到底经过哪些地方?
你要改创建订单的校验规则,就不能只看Service。你还要知道入口参数从哪里来、异常结果回到哪里、数据最终有没有写库。
第四步:生成阅读路线
有了分层和调用链之后,ReadingPathGenerator输出阅读路线:
json
{
"projectName": "legacy-order",
"entrypoints": ["OrderController#createOrder"],
"layers": ["controller", "service", "mapper"],
"callChain": [
"OrderController#createOrder",
"OrderService#createOrder",
"OrderMapper#insert"
],
"readingPath": [
"先读 OrderController#createOrder,理解请求入口和参数。",
"再读 OrderService#createOrder,理解核心业务规则和异常分支。",
"最后读 OrderMapper#insert,理解数据落库和持久化边界。"
]
}
这就是一张很朴素的代码阅读地图。它不替你下结论,只把"先看哪里、再看哪里、每一步确认什么"讲清楚。
规则扫描和AI应该怎么分工
这讲最重要的设计是分工:
text
规则扫描负责事实
AI 负责表达和归纳
人负责确认和决策
规则扫描适合做确定性工作:
text
类名、路径、入口方法、调用链、分层
可见的异常分支、可见的字段痕迹
AI适合做表达性工作:
text
把阅读路线讲清楚
总结业务含义
提示可能风险
生成新人可读的解释
围绕代码事实做问答
人负责最后判断:
text
这个兼容分支还能不能删?
这个兜底返回是否合理?
这个字段是否仍被线上报表依赖?
这次需求到底应该改哪一层?
这样分工之后,AI才更像一个可靠助手,而不是一个凭空猜项目的回答机器。
这类能力能解决哪些问题
第一,新人接手老项目。 以前新人拿到项目,只能靠同事口头讲。如果系统能自动生成阅读路线,新人至少有一个起点。
第二,需求改造前做影响分析。 比如要改创建订单的参数校验。阅读路线会提醒你:入口参数在Controller、quantity校验在Service、clientVersion兼容分支也在Service、落库动作在Mapper。这能减少"只改一处,以为完了"的风险。
第三,为后续AI能力建立上下文。 后面做代码评审、RAG问答、工单分析,不能只给AI一个孤立类。更稳的是先给它:入口、调用链、分层、关键规则、异常分支、数据边界。这样AI的回答才更像"基于项目事实的建议",而不是"基于代码片段的猜测"。
从Demo到落地,还差什么
本讲Demo只跑通了"扫描→分层→调用链→阅读路线"的事实层,但真实项目落地还差几件事:
大项目增量扫描:Demo是全量扫描,企业项目往往有几千个Java文件,每次改动全量扫描不现实。需要增量扫描------只处理改过的文件,用缓存记录历史结构事实。
跨模块调用链:Demo只处理了单链路(Controller→Service→Mapper),真实项目有跨Service调用、异步事件、AOP拦截、Spring Bean注入。需要把这些关系也纳入调用链分析。
静态分析升级到字节码层:类名规则能区分Controller/Service/Mapper,但覆盖不了接口实现类、抽象方法、多态调用。生产级方案需要基于ASM或Javassist做字节码分析。
真实模型接入做表达层:当前Demo的ReadingPathGenerator输出的是结构化JSON。后续需要接入真实模型,把这份JSON翻译成自然语言的新人引导、风险提示和问答响应。
如果你正在推进老系统的AI辅助阅读能力,这几件事是绕不开的。完整方案会在后续更新中逐步展开。
企业避坑
- 不要把完整源码一次性丢给模型 --- 大项目代码量太大,模型上下文有限,安全风险也更高。更稳的方式是先扫描、切分、摘要,再让模型基于摘要解释。
- 不要让模型凭空判断调用链 --- 调用链、类名、方法名、入口方法这些结构化事实,应该尽量由规则或静态分析生成。
- 不要只解释单个类 --- 老系统最难的是链路,不是单个类。AI输出应该围绕"业务动作经过哪些地方"来组织。
- 不要忽略异常分支和历史兼容 --- 很多生产问题不是主流程错了,而是兼容逻辑、兜底逻辑、历史字段没有读懂。
- 不要让AI直接给改造结论 --- 更好的顺序是:先生成阅读路线 → 再确认代码事实 → 再让AI总结风险 → 最后由人决定怎么改。
小结
第4讲是这个系列里的一个转折点。
前3讲讲的是AI如何安全接入老系统。第4讲开始补另一个基础:AI和团队如何先读懂老系统。
这一讲的Demo只跑事实层,不跑真实模型。这不是退一步,而是把地基先打牢。
因为后续所有更复杂的AI能力,都应该建立在可验证的项目事实上。
| 后续讲次 | 和本讲的关系 |
|---|---|
| 第5讲 代码评审 | 评审前先用阅读路线确认入口、调用链和影响范围 |
| 第6讲 RAG知识库 | 文档切分和索引要依赖项目结构,而不是随便按文件切 |
| 第7讲 工单Agent | 工单定位问题前,要知道业务动作对应哪条代码链路 |
| 第8-9讲 Browser Agent / Workflow | Agent执行动作前,必须先理解代码上下文和系统边界 |
| 第10讲 多Agent研发团队 | 多Agent协作需要共享同一份结构化项目事实 |
不要让AI先猜。先给AI一张结构化的代码地图。只有解释有依据,AI才能真正进入企业老系统的研发流程。