当技术具有了情怀:认识 LingFrame——一个为老系统“续命”的 JVM 治理框架

写在前面

如果你也是一个 Java 开发者,你一定见过这样的系统:

它已经跑了五六年,代码庞大到没人敢动。每一次上线都像在黑夜中摸索,每一次发布都伴随着祈祷。你想重构,但业务不允许停下;你想拆分微服务,但成本和风险都太高。

于是它就这么跑着,像一个越来越臃肿、越来越脆弱的老人,你知道它总有一天会出问题,但你无能为力。

直到我遇到了 LingFrame (点击下方链接访问)。

📦 项目地址

GitCode

https://gitcode.com/lingframe/LingFrame

Gitee

https://gitee.com/knight6236/lingframe

GitHub

https://github.com/LingFrame/LingFrame

在这里插入图片描述


一、一个不一样的 README

第一次看到 LingFrame 的 README,我愣了一下。

它不是从“如何集成”开始的,而是这样写的:

“很多系统并不是设计得不好,只是活得太久,改得太急。”

“每一次改动都像在黑夜中摸索,每一次上线都伴随着祈祷。”

这段话击中了我。它说的不就是我们每天都在面对的现实吗?

这个项目叫 LingFrame(灵珑),一个面向长期运行系统的 JVM 运行时治理框架。

但让我决定深入了解的,不是它的技术特性,而是它的气质——它似乎真的懂那些在老旧系统里挣扎的程序员。


二、🔍 LingFrame 是什么:给 Spring Boot 装上“治理内核”

LingFrame · 灵珑

项目核心信息 兼容版本支持 代码仓库地址 参与贡献
🟢 状态:核心功能已实现
📜 许可证:Apache 2.0
🟠 Java:8 / 17
🟢 Spring Boot:2.7.18 / 3.5.6
🔴 Gitee 主仓库
AtomGit G-Star 孵化项目
GitHub 镜像仓库
🎉 PRs 欢迎提交
💡 DeepWiki 技术问答

🎯 一句话概括

这个项目叫 LingFrame(灵珑)

一个JVM 运行时安全治理解决方案。

它不做微服务那样的物理拆分(不需要网络调用,没有分布式事务的烦恼),而是在逻辑上帮你把业务模块拆分成 独立的单元(Ling)

🧩 什么是“单元”?

可以把“单元”理解成一个轻量级的、可热插拔的 Spring Boot 应用

维度 说明 对开发者的意义
类加载器 每个单元拥有独立的 ClassLoader 再也不用担心 jar 包冲突,不同单元可以用不同版本的依赖
Spring 上下文 每个单元有完全独立的 ApplicationContext Bean 不会互相污染,一个单元的 @Component 扫不到另一个单元里去
生命周期 可独立加载、启动、停止、卸载 出问题的单元可以单独干掉,不影响主系统
版本管理 支持同一单元多版本共存 蓝绿部署、A/B 测试天然支持
治理策略 每个单元可配置独立的权限、熔断、限流规则 高风险的单元可以限制得更严,核心单元保障高可用

这意味着什么?

你可以在不重启整个应用的情况下:

  • 给订单模块单独升级
  • 给用户模块单独回滚
  • 给支付模块单独加权限控制
  • 让 10% 的用户先体验新版本的功能

🏗️ 三层架构:谁负责什么,边界在哪

LingFrame 的核心是三层架构,每一层职责单一,像操作系统一样清晰:

架构分支

💡 这个架构想明白了什么?

第一,Core 不做业务。

这是最关键的决策。Core 里没有任何业务代码,它只做三件事:调度、仲裁、记录。就像操作系统的内核,不关心你是写文档还是打游戏,只负责分配 CPU 时间片、管理内存、记录日志。

好处是什么?Core 可以保持稳定,不会因为业务变化而频繁修改。它一旦稳定下来,就可以成为整个系统的“信任基座”。

第二,所有跨单元调用必须经过 Core。

这意味着:没有后门可走。A 单元想调 B 单元的服务?必须经过 Core。A 单元想查数据库?必须经过 Core。每一条调用链路都在 Core 的掌控之下。

第三,基础设施也被“代理”了。

业务单元不能直接操作数据库、Redis,而是通过 Infrastructure 层的代理。这些代理在真正执行操作之前,会先问 Core:“这个单元有权限这么做吗?” Core 查一下配置,说“可以”才放行,说“不行”就直接拦下。


三、它是怎么做到的:技术思路拆解

1. 🧱 类加载隔离:解决“jar 包冲突”的思路

要解决的问题:

一个系统里,订单模块用 FastJSON 1.x,支付模块用 FastJSON 2.x。如果放在同一个 ClassLoader 里,必然冲突,总有一个模块会报 NoSuchMethodError

LingFrame 的思路:

让每个单元用自己的 ClassLoader 加载自己的依赖,互不干扰。

但这里有个坑:如果两个单元之间要传对象(比如 UserDTO),这个对象由谁加载?A 的 ClassLoader 加载的 UserDTO,B 的 ClassLoader 不认识,一传就报 ClassCastException

解法:

把接口和 DTO 单独抽出来,放到一个“共享区域”,由所有单元共用的父 ClassLoader 加载。业务实现类(有版本冲突风险的)由各自的 ClassLoader 加载。

这样:

  • 订单单元用的 FastJSON 1.x,支付单元用的 FastJSON 2.x,互不影响 ✅
  • 两个单元传的 UserDTO 是同一个 ClassLoader 加载的,不会类型转换异常 ✅

配置方式:

application.yaml 里告诉框架,哪些 jar 是共享 API:

lingframe:
  preload-api-jars:
    - libs/order-api.jar      # 订单模块的接口和 DTO
    - libs/user-api.jar       # 用户模块的接口和 DTO
    - libs/*-api.jar          # 支持通配符

2. 🌱 Spring 上下文隔离:不是父子,是兄弟

要解决的问题:

每个单元需要有自己的 Spring 容器,但如果用传统的父子上下文,子容器可以拿到父容器的 Bean,这就绕过了权限控制。而且父容器持有子容器的引用,单元卸载时无法释放内存。

LingFrame 的思路:

每个单元的 Spring 上下文都是完全独立的,和 Core 的上下文没有父子关系。

单元 A 的 Bean 拿不到 Core 的 Bean,单元 B 的 Bean 也拿不到单元 A 的 Bean。想调用别人的服务?必须通过 @LingReference 走代理,不能直接 @Autowired

这样:

  • 权限控制不会被绕过 ✅
  • 单元卸载时没有引用残留,可以被 GC 回收 ✅
  • 每个单元可以有自己的配置、自己的 Bean,完全隔离 ✅

3. 🔌 跨单元调用:@LingReference 的设计思路

要解决的问题:

订单单元想调用户单元的服务,最直接的方式是 @Autowired 一个 UserService,但这样有耦合,而且用户单元可能还没启动。

LingFrame 的思路:

@LingReference 注入一个代理对象,真正调用时才去解析目标单元。

这个设计妙在哪里?

特性 说明 对开发者的好处
延迟绑定 调用发生时才去找目标单元 单元启动顺序无关,A 启动时 B 没启动也不报错
智能路由 可以根据配置决定调哪个版本 灰度发布、A/B 测试天然支持
治理拦截 调用前自动做权限检查、熔断判断 业务代码里不用写这些重复逻辑

使用体验:

@Component
public class OrderService {
    
    @LingReference
    private UserQueryService userQueryService;  // 就像调本地方法
    
    public Order createOrder(String userId) {
        // 这一行背后:权限检查、审计日志、可能还有熔断判断
        UserDTO user = userQueryService.findById(userId);
        return new Order(user);
    }
}

4. 🛡️ 基础设施代理:零信任的落地思路

要解决的问题:

业务单元应该只能做它被允许做的事。比如用户单元可以查数据库(读),但不能删表(写)。但常规的 Spring 应用里,只要有了 DataSource,什么 SQL 都能执行。

LingFrame 的思路:

在业务单元和基础设施之间加一层代理,所有操作必须经过代理的检查。

以数据库为例:

当业务单元执行 userRepository.findById(1L) 时,真实发生的流程是:

  1. 框架自动包装的 LingDataSource 接管了调用
  2. 它从线程上下文里拿到“谁在调”(user-ling 单元)
  3. 解析要执行的 SQL 类型(SELECT 是读操作)
  4. 问 Core:“user-ling 有数据库读权限吗?”
  5. 有权限 → 放行,执行真正的查询
  6. 没权限 → 直接抛异常,SQL 根本不会执行

整个过程对业务代码完全透明,开发者写 userRepository.findById() 时,根本感知不到这些拦截。

缓存也是同理:

redisTemplate.opsForValue().set(key, value) 会被拦截,框架根据方法名(set)推导出是写操作,检查权限后再决定是否放行。


5. 🔐 权限治理:声明式 + 智能推导的思路

要解决的问题:

每个单元需要什么权限,应该明确说出来,而不是在代码里到处写 if(check)

LingFrame 的思路:

单元在 ling.yml 里声明自己需要哪些权限:

governance:
  permissions:
    - capability: "storage:sql"    # 数据库操作
      permission: "READ"            # 只读
    - capability: "cache:redis"     # Redis 操作
      permission: "WRITE"           # 可写

如果没声明怎么办?

框架会“智能推导”。比如方法名叫 findUserById,推导为读操作;叫 createUser,推导为写操作。推导规则可配置,也可以完全关闭,强制要求显式声明。

开发模式 vs 生产模式:

  • 开发模式:权限不足时只警告,不阻断。避免调试时频繁被打断。
  • 生产模式:严格执行,权限不足直接拒绝。

这个细节体现了对开发者的体谅——既要安全,又不影响开发效率。


6. 🔄 蓝绿部署与灰度发布:不停机的思路

要解决的问题:

新功能上线,万一出问题怎么办?能不能只让一部分用户先试?

LingFrame 的思路:

单元支持多版本共存。安装新版本时,旧版本继续处理已有请求;新版本启动完成后,新请求切换到新版本;旧版本等请求处理完再卸载。

灰度发布的配置方式:

canary:
  enabled: true
  rules:
    - type: "percentage"        # 按百分比
      value: 10                  # 10% 流量
      targetVersion: "2.0.0"     # 走新版本
    - type: "header"             # 按请求头
      key: "X-User-Tag"
      value: "beta"
      targetVersion: "2.0.0"

7. ⚡ 弹性治理:熔断、限流、重试的思路

要解决的问题:

一个单元出问题,不能让它拖垮整个系统。调用量突增时,不能把后端打爆。

LingFrame 的思路:

内置一套完整的弹性治理能力,对业务代码完全透明:

能力 实现思路 配置方式
熔断 滑动窗口统计错误率,超阈值后自动断开 circuit-breaker: enabled: true
限流 令牌桶算法,控制请求速率 rate-limiter: permits-per-second: 100
重试 失败自动重试,可配置退避策略 retry: max-attempts: 3
超时 每个服务可单独配置超时时间 @LingService(timeout = 3000)

这些能力都在代理层自动完成,业务代码里写的是纯业务逻辑,不需要关心这些横切关注点。


四、为什么说它有“情怀”

技术层面讲完了,我想聊聊它给我的另一种感受。

LingFrame 的文档里有很多“不像技术文档”的话:

“你不需要一次性读完所有内容。灵珑允许你在任何阶段停下。”

“灵珑不会替系统做决定。她只是在系统还愿意被理解的时候,帮你把事情放回该在的位置上。”

“如果你只是走到这里停下,那也完全没有关系。”

这些话让我觉得,这个项目的作者真的在乎使用它的人

很多开源项目追求“功能强大”、“性能极致”,但 LingFrame 在追求这些的同时,还在追求另一件事:让开发者少受一点苦

它的设计处处体现着这一点:

设计 背后的体谅
开发模式 权限不足只警告不阻断,知道你在调试,不打断你
消费者驱动契约 谁需要能力谁定义接口,不用被迫适配别人的全量接口
@LingReference 延迟绑定 单元启动顺序无关,A 启动时 B 没启动也不报错
三层 ClassLoader 帮你解决 jar 包冲突,不用自己折腾
异步审计 不阻塞业务调用,性能影响降到最低

这些不是“技术难点”,而是“开发者痛点”。LingFrame 在意的,正是这些。


五、我为什么参与进来

我承认,最开始是被它的“气质”吸引的。

但在使用的过程中,我发现它的技术设计同样扎实。三层架构清晰,文档完整(甚至可以说优美),代码质量高,issue 响应及时。

于是我开始提 PR。

从一个小的文档修正,到一个功能的讨论,再到更深度的参与。我发现这个项目的社区氛围和它的代码一样——尊重人

没有那种“你不行”的高傲,没有“爱用不用”的冷漠。讨论技术问题时,大家关注的是“怎样对使用者更好”。

这让我想起 README 里的那句话:

“灵珑存在的意义,是在系统复杂到某个阶段时,为‘回缩’与‘重组’提供可能性。”

它给老系统“续命”,也在给开发者“减负”。


六、如果你想了解更多

LingFrame 目前已经实现了:

阶段 核心能力 状态
Phase 1 核心治理:权限、审计、单元隔离 ✅ 已完成
Phase 2 可视化:Dashboard 治理中心 ✅ 基本完成
Phase 3 弹性治理:熔断、降级、重试、限流 ✅ 已完成
Phase 4 可观测性:指标采集、调用链可视化 ⏳ 计划中
Phase 5 基础设施扩展:消息代理、搜索代理 ⏳ 计划中

🚀 快速体验

# 克隆代码
git clone https://gitee.com/knight6236/lingframe.git

# 编译安装
cd lingframe
mvn clean install -DskipTests

# 启动示例应用
cd lingframe-examples/lingframe-example-lingcore-app
mvn spring-boot:run

启动后访问:http://localhost:8888/dashboard.html,就可以看到效果。

更详细的教程在 getting-started.md,5 分钟就能跑起来。


七、欢迎一起参与

LingFrame 还在成长中。

如果你也被老系统折磨过,如果你想了解一种不一样的治理思路,欢迎来看看。

如果你觉得这个方向有意义,也欢迎参与贡献——可以是代码,可以是文档,可以是 issue 讨论,甚至只是点个 Star。

文档里有一句话我很喜欢,用它来结尾吧:

“LingFrame 不要求你一开始就‘治理一切’。”

“它只是让你在系统失控之前,多一次选择的机会。”


项目负责人:knight
测试/文章撰写:雪豹同志/凌云拓界

欢迎在评论区交流讨论!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐