「不用发版也能改代码」—— dynamo-spring:Java 运行时动态编译与 Bean 管理工具包

想象这几个场景

  • 线上 Service 有个逻辑 bug,改一行代码却要跑完「打包 → 审批 → 灰度 → 全量」的完整发版流程;
  • SaaS 平台要给每个租户注册不同的 Controller 路由,每次新增租户都要重启应用;
  • 规则引擎的条件分支用 Java 写死,每次调整规则都要重新部署;
  • 想给现有服务加一段临时监控逻辑,发版成本比写代码本身还高。

这些场景有一个共同诉求:在运行时动态加载代码,最好不用重启。

Java 本身支持运行时编译(javax.tools.JavaCompiler),Spring 也开放了 Bean 注册的入口。但要把这两件事做到生产可用——内存字节码管理、ClassLoader 隔离、Spring MVC 路由热注册、生命周期回收——需要填的坑不少。

dynamo-spring 就是为这个诉求而生的。

痛点:一个改一行的需求,要走一天的流程

我们都经历过:线上某个 Service 的判断逻辑有 bug ,影响面不大,但修复代码只有两三行。按公司规范,要走完整发版流程——本地跑测试、打 fat jar、提交变更单、等审批、灰度 10%、全量发布、观察监控。一个「改一行」的需求,从提交代码到线上生效,快的半天,慢的隔天。

有没有可能跳过发版流程,直接在运行时把新类加载进去?

答案是 Java 本身就能做到。JDK 自带的 JavaCompiler 可以在运行时编译源码,配合自定义 ClassLoader 把字节码加载到内存。Spring 的 BeanFactory 也支持运行时注册 Bean。理论上,一条链路就能实现「改完即生效」。

dynamo-spring 把这条链路做成了开箱即用的工具库。

dynamo-spring 是什么

dynamo-spring 是一个 Java 工具库,提供三大能力:

能力 说明
动态编译 运行时编译 Java 源码,字节码保存在内存中,通过会话模型 ClassLoader 加载,无需落盘 .class 文件
AOP 代理 基于 ByteBuddy 的方法拦截,提供 before / after / afterThrow 三个增强点
Spring Bean 管理 运行时注册/注销单例 Bean 和 Controller 路由,通过子类化 Spring MVC 官方扩展点实现

库分两个模块,按需选择:

模块 适用场景
dynamo-spring-core 纯 JDK 环境,只要动态编译 + AOP 代理
dynamo-spring-spring-boot-starter Spring Boot 项目,三大能力 + 自动配置

已发布到 Maven Central,JDK 17+,Spring Boot 3.5+。

5 行跑通

引入依赖后,5 行代码跑通动态编译:

import cn.wubo.dynamo.spring.DynamoRuntime;

try (DynamoRuntime runtime = DynamoRuntime.create()) {
    Class<?> clazz = runtime.compileAndLoad(
        "public class A { public String hi() { return \"hi\"; } }");
    Object o = clazz.getDeclaredConstructor().newInstance();
    System.out.println(o.getClass().getMethod("hi").invoke(o)); // hi
}
java
 
运行

不需要提前编译,不需要落盘 .class,源码字符串直接变成可执行对象。

三大能力速览

1. 动态编译:compiler 包

DynamoClassLoader 是会话模型——每个实例独立持有 classpath 和字节码缓存,try-with-resources 关闭后自动清理。

try (DynamoClassLoader loader = DynamoClassLoader.create()) {
    Class<?> clazz = loader.compileAndLoad("""
        public class RiskRule {
            public boolean check(int amount) { return amount > 10000; }
        }
        """);
    Object rule = clazz.getDeclaredConstructor().newInstance();
    boolean blocked = (boolean) clazz.getMethod("check", int.class).invoke(rule, 50000);
    // blocked = true
}
java
 
运行

编译失败会抛出 CompilationException,携带完整的诊断信息,方便在运行时捕获并反馈给调用方。

2. AOP 代理:aspect 包

基于 ByteBuddy 的方法拦截。用内置的 SimpleAdvice 一行创建代理,自动记录方法执行耗时:

MyService target = new MyServiceImpl();
MyService proxy = DynamoProxy.proxy(target, new SimpleAdvice());

proxy.doSomething(); // before → 执行 → after,耗时自动记录
java
 
运行

需要更细粒度的控制?实现 IAdvice 接口,拿到 before / after / afterThrow 三个增强点,想做什么做什么。

3. Spring Bean 管理:bean 包

不反射 Spring 私有 API ——通过子类化 RequestMappingHandlerMapping,走 Spring 官方推荐的 WebMvcRegistrations 扩展点。注册进去的 Controller 路由立刻生效,不用重启。

DefaultListableBeanFactory bf = (DefaultListableBeanFactory) ctx.getBeanFactory();

// 注册一个 Controller,路由立即生效
DynamoBean.registerController(bf, "emergencyCtrl", EmergencyController.class);

// 不再需要时移除路由
DynamoBean.unregisterController(bf, "emergencyCtrl");
java
 
运行

典型用法:线上紧急加一个临时接口、灰度新功能、多租户按需注册不同路由。

几个设计亮点

最后提几个值得关注的设计决策:

  • ClassLoader 用会话模型,不是单例。每个 DynamoClassLoader 实例独立持有字节码缓存,try-with-resources 关闭后自动清理。多个动态编译任务互不干扰,也不用担心内存泄漏。
  • 不反射 Spring 私有 API。动态 Controller 路由通过子类化 RequestMappingHandlerMapping + WebMvcRegistrations 注入,升级 Spring Boot 版本时不需要重新验证反射点。
  • ByteBuddy 而非 CGLIB。原生 ByteBuddy,不是 Spring 重新打包版本,对 JDK 21+ 支持更及时。

上手试试

<dependency>
    <groupId>io.github.wb04307201</groupId>
    <artifactId>dynamo-spring-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>
xml
 
try (DynamoRuntime runtime = DynamoRuntime.create()) {
    Class<?> clazz = runtime.compileAndLoad("public class A { public String hi() { return \"hi\"; } }");
    Object o = clazz.getDeclaredConstructor().newInstance();
    System.out.println(o.getClass().getMethod("hi").invoke(o)); // hi
}
java
 
运行

项目开源于 Apache 2.0 协议。觉得有用,欢迎去 GitHub 点个 Star ⭐

  • GitHub:https://github.com/wb04307201/dynamo-spring
  • Gitee:https://gitee.com/wb04307201/dynamo-spring

「不用发版也能改代码」—— dynamo-spring:Java 运行时动态编译与 Bean 管理工具包-CSDN博客

posted @ 2026-10-15 22:54  charyGao  阅读(7)  评论(0)    收藏  举报