为什么越来越多公司在去 Spring?
Spring 没有错。它只是太重了——在一个 Serverless 按毫秒计费、容器镜像按 MB 收费的时代。
先说一个让人不舒服的真相
你的 Spring Boot 应用,上线之后:
-
冷启动要 6~12 秒
-
JVM 稳定后内存占用 200~400MB
-
Docker 镜像动辄 150MB 起步
-
每次
mvn package完你都要等它扫描一遍 classpath
这些数字放在 2015 年无所谓,放在今天就是钱。
AWS Lambda 按 100ms 计费,你的函数光启动就花掉了 60 个计费单位。K8s 上跑 50 个 Pod,每个多吃 100MB,那是 5GB 白白烧掉。
于是,一场悄悄的叛逃正在发生。
Spring 到底重在哪里?
很多人骂 Spring 重,但说不清楚重在哪。
核心原因只有一个:运行时反射。
Spring 的 IoC 容器在应用启动时做了一件很"聪明"但很"慢"的事情:
这个流程每次启动都要完整跑一遍。而且反射在 JVM 里天生慢,因为它绕过了编译器的所有优化。
更要命的是,为了支持动态代理(AOP、事务、懒加载),Spring 在运行时会生成大量字节码,这些都要吃内存。
你以为你在用 Spring,其实你在养一个全天候运转的反射引擎。
怎么解决?
Quarkus:把工作挪到编译期
Quarkus 是红帽(Red Hat)2019 年推出的,口号是 **"Kubernetes Native Java"**。
它的核心思路是:既然每次启动都要做的事,为什么不在编译时做完?
// Spring 的做法:运行时解析
@Service
public class OrderService {
@Autowired
private OrderRepository repo; // 启动时反射注入
}
// Quarkus 的做法:编译期生成注入代码
@ApplicationScoped
public class OrderService {
@Inject
OrderRepository repo; // 构建时生成 OrderService_Bean.java
}
Quarkus 在 mvn package 阶段就把依赖关系分析完毕,生成好注入代码。启动时直接加载,根本不需要反射。
结果:同等应用,启动时间从 6s 降到 0.8s。
更狠的是它支持 GraalVM 原生镜像编译:
./mvnw package -Pnative
# 编译需要 3 分钟,但生成的二进制文件
# 启动只需要 0.04 秒
# 内存占用从 280MB 降到 35MB
Lambda 函数、边缘计算场景,这个差距是决定性的。
Micronaut:注解处理器的魔法
Micronaut 来自 Grails 的作者,走的路子和 Quarkus 类似,但实现方式略有不同。
它用的是 Java 注解处理器(APT),在编译期直接生成 BeanDefinition 类:
你写的代码:
@Singleton
public class UserService { ... }
编译后自动生成:
$UserServiceDefinition.java ← 包含所有依赖信息
$UserServiceDefinitionClass.java ← 包含类型元数据
没有反射,没有动态代理,没有 classpath 扫描。
Micronaut 对 测试的支持也很友好。它有一个专门为微服务测试设计的 @MicronautTest,不需要启动完整上下文,测试速度比 Spring Boot 快 3~5 倍。
Helidon:Oracle 的亲儿子
Helidon 是 Oracle 在 2018 年开源的,分两个版本:
-
Helidon SE:响应式、函数式,极简主义,没有注解
-
Helidon MP:实现了 MicroProfile 规范,有注解,更像 Spring
// Helidon SE 风格:函数式,没有魔法
Routing routing = Routing.builder()
.get("/health", (req, res) -> res.send("ok"))
.post("/orders", this::createOrder)
.build();
WebServer.create(routing).start();
// 启动完了,就这些
Helidon 的定位很明确:Oracle 云 + Jakarta EE 生态的亲密伴侣。如果你的基础设施在 OCI 上,或者你需要遵循企业级 MicroProfile 规范,Helidon 是自然选择。
三者横向对比

轻量化框架的代价——没有人告诉你的另一面
说了这么多优点,该说说坑了。
坑一:编译期处理破坏了动态性
Spring 最强大的特性之一是运行时的灵活性:你可以在运行时注册新的 Bean、动态替换实现、用 AOP 做任意切面。
Quarkus 和 Micronaut 把这些都在编译期固化了。这意味着:
-
大量依赖反射的第三方库直接不能用
-
运行时动态代理受限
-
某些 Spring 的高级用法(
BeanDefinitionRegistryPostProcessor等)没有对应实现
我见过团队把一个用了 cglib 动态代理的老库接到 Quarkus 里,调试了两周没搞定,最后回退。
坑二:生态碎片化,你总会踩到"这个没有"
Spring 的生态覆盖率接近 100%。你想用的任何东西,几乎都有 Spring Boot Starter。
Quarkus 的 Extension 列表很长,但边缘场景就不一定了。比如某些老牌中间件的 Java 客户端,和 GraalVM 原生镜像的兼容性就是个问题:反射调用、动态类加载——原生镜像一个都不支持。
坑三:原生镜像编译慢到让人崩溃
GraalVM native-image 编译一个中等规模应用需要 3~8 分钟,内存峰值消耗超过 8GB。
你的 CI/CD 流水线每次 push 都要跑这个,构建机器要足够强,否则比 Spring Boot 慢更多。
坑四:调试体验倒退了十年
没有了运行时字节码增强,你用 Quarkus 调试的时候会发现堆栈信息变得奇怪,某些断点打不上,热重载在原生模式下不存在。
什么情况下值得去 Spring?
诚实地说,以下场景才真正值得切换:
值得切换:
-
Serverless 函数,冷启动直接影响用户体验或成本
-
边缘计算节点,资源极度受限(512MB 内存)
-
大规模微服务集群,每个节点省 100MB,乘以 500 个 Pod
-
新项目,团队愿意接受学习成本
不值得切换:
-
已有大量 Spring 代码,迁移成本远超收益
-
大量使用动态特性的老系统
-
团队对新框架没有学习时间
-
业务逻辑复杂,第三方依赖多而杂
最诚实的建议:如果你的应用跑在常驻容器里,启动时间根本不是问题,内存也够用,Spring Boot 就很好。不要为了追新而迁移。
Spring 自己也没闲着
不得不提的是,Spring 团队非常清楚这个压力。
Spring Boot 3.x + GraalVM 原生镜像支持已经正式落地。虽然支持程度还不如 Quarkus 成熟,但差距在缩小。
Spring 6.x 引入的 AOT 引擎,也在尝试把部分工作挪到编译期:
// Spring 6 AOT:提前生成 BeanDefinition
// spring-aot-maven-plugin 在构建期处理
// 启动时减少反射使用
这场战争还没结束,Spring 还在反击。
给技术决策者的一张选型卡
最后说一句
去 Spring 不是因为 Spring 烂,而是因为场景变了。
Serverless、边缘计算、超大规模微服务——这些场景对启动时间和内存的要求是真实的、经济上有意义的。
但如果你的业务跑在普通的 K8s 集群里,容器常驻不重启,用户也不会感知启动时间,那 Spring 依然是那个最稳、生态最全、出了问题最容易找到答案的选择。
技术选型从来不是比谁更先进,而是比谁更适合你的问题。
更多推荐


所有评论(0)