大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
Quarkus 入门
这一课你将能做到
- 创建并运行一个最小 Quarkus REST 项目,理解 JAX-RS 注解与开发态热重载的工作方式
- 说出 JVM 模式与 Native 模式的取舍,解释 Native Image 为什么启动快、代价是什么
- 把 Spring 的概念(@RestController、@Autowired、application.properties)一一映射到 Quarkus 对应物
为什么要学
Quarkus 自称「Supersonic Subatomic Java」:开发态毫秒级热重载,生产态可选 GraalVM Native Image 把应用编译成原生可执行文件,冷启动从几秒压到几十毫秒、内存占用大幅下降——这正是 Serverless、大规模容器调度、按量付费场景最关心的指标。
对 Spring 用户的价值不在「换框架」,而在「看懂另一个答案」:Quarkus 把 DI、配置、HTTP 层的处理从运行期挪到构建期(AOT 思路),对照之后你会更清楚 Spring 的反射魔法究竟花了什么成本。面试聊「AOT vs JIT」「云原生 Java」时,这就是你的差异化谈资。
先建立图像
传统 JVM 模式 Native 模式
┌──────────────────────────┐ ┌──────────────────────────┐
│ OS → JVM → 字节码 │ │ OS → 原生可执行文件 │
│ 启动:类加载+解释+JIT 预热 │ │ 启动:直接机器码执行 │
│ 冷启动:秒级 │ │ 冷启动:几十毫秒 │
│ 峰值性能:JIT 优化后最高 │ │ 峰值:略低于充分 JIT 预热 │
│ 构建:快 │ │ 构建:慢(分钟级)+需 GraalVM │
└──────────────────────────┘ └──────────────────────────┘
适合常规部署、长跑服务 适合 Serverless/缩容到零/边缘
跟着做
步骤 1:创建项目并进入开发模式
用 Quarkus CLI 或 code.quarkus.io 生成:
quarkus create app com.example:hello-quarkus --extension='rest-jackson'
cd hello-quarkus
./mvnw quarkus:dev
没有 CLI 时,打开 code.quarkus.io 选 REST Jackson 扩展下载 zip,解压后同样执行 ./mvnw quarkus:dev(Windows 用 mvnw.cmd)。开发模式下改代码保存即生效,无需重启——体验与 Spring Boot DevTools 类似但更彻底。
步骤 2:写一个 REST 资源 + CDI 服务
src/main/java/com/example/OrderService.java:
package com.example;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped // CDI 的单例作用域,对应 Spring 的 @Service + singleton
public class OrderService {
public String statusOf(long id) {
return id % 2 == 0 ? "SHIPPED" : "PAID";
}
}
src/main/java/com/example/OrderResource.java:
package com.example;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/orders") // JAX-RS:类级路径
public class OrderResource {
private final OrderService service;
public OrderResource(OrderService service) { // 构造器注入:单构造器可省 @Inject
this.service = service;
}
@GET
@Path("/{id}") // 方法级路径
@Produces(MediaType.APPLICATION_JSON)
public OrderView get(@PathParam("id") long id) {
return new OrderView(id, service.statusOf(id));
}
public record OrderView(long id, String status) {} // record 直接做 JSON 响应体
}
访问 http://localhost:8080/orders/1,返回 {"id":1,"status":"PAID"}。改动 statusOf 的实现,保存后刷新页面即见新结果——热重载覆盖普通类改动。
步骤 3:配置与打包
src/main/resources/application.properties:
quarkus.http.port=8080
order.greeting=hello
打包与运行:
./mvnw package # 产出普通 jar(默认 JVM 模式)
java -jar target/quarkus-app/quarkus-run.jar
可选:装好 GraalVM 后构建 Native(构建慢属正常,分钟级):
./mvnw package -Dnative
./target/hello-quarkus-1.0.0-SNAPSHOT-runner # 直接是原生可执行文件
启动日志里能看到毫秒级 started in,对比 jar 模式的启动时间,这就是 Native 的直观证据。
步骤 4:跑一次开发态测试(选做)
Quarkus 项目自带测试骨架 src/test/java,./mvnw test 即可;quarkus:dev 下还提供连续测试模式。测试不依赖容器预启动,启动成本同样受益于 Quarkus 的快速引导。
讲清楚要点
- Quarkus 的核心思路是构建期偏移:把 Spring 在运行期做的事(扫描 Bean、生成代理、解析配置、建立路由表)尽量挪到构建期,启动时只做装配——启动快、内存低、Native 可行,三个收益同源。
- JAX-RS 是标准 API:
@Path/@GET/@Produces属于 Jakarta RESTful Web Services 规范,Quarkus 用 RESTEasy Reactive 实现。注解与 Spring MVC 不同,HTTP 语义完全一致,迁移是「换注解」而不是「换思维」。 - CDI 是标准 DI:
@ApplicationScoped≈ Spring singleton Bean,@Inject≈ @Autowired;第 9 课的构造器注入在这里同样是默认推荐,单构造器可省略 @Inject。 - JVM vs Native 取舍:JVM 模式构建快、峰值性能高(JIT 充分预热后),适合长跑服务;Native 冷启动毫秒级、内存小、无 JIT 预热,适合 Serverless/缩容到零。多数常规后端服务,JVM 模式就够。
- Native 的封闭世界假设:构建时必须确定所有可达代码,运行期反射、动态代理、动态类加载受限——这就是 Spring 应用做 Native 需要 AOT 处理引擎补元数据的原因,Quarkus 靠扩展在构建期消化掉了大部分需求。
- 选型务实:国内就业与站内项目主线仍是 Spring Boot;Quarkus 定位是「知道原理 + 需要时能评估 POC」,不必为学而学替换存量技术栈。
你可能会懵的地方
- AOT 和 JIT 到底差在哪? JIT(即时编译):运行期统计热点把字节码编译成机器码,峰值性能高但要预热,启动慢;AOT(提前编译):构建期直接生成机器码,启动即峰值、无预热,但无法做基于运行时画像的激进优化,且放弃部分动态能力。
- Quarkus 的 dev 模式为什么这么快? 构建期已完成大部分静态工作,dev 模式只对改动的类做局部重建与热替换;配合 live reload 通道,改动到生效是毫秒级。
- Spring 项目能直接上 Native 吗? Spring Boot 3 起官方支持 GraalVM Native(Spring AOT),但要求依赖与配置对反射友好、构建时间显著变长、部分库需额外元数据;存量项目是「评估后局部试点」,不是一键切换。
- Quarkus 和 Micronaut 是竞品吗? 同属构建期优化的现代框架,Quarkus 主打 GraalVM/云原生生态整合(Kubernetes、Dev Services),Micronaut 主打编译期 DI 注解处理器。思路相似,生态侧重不同。
- record 能在 Quarkus 里当响应体吗? 能,RESTEasy Reactive + Jackson 原生支持 record 序列化,与 Spring Boot 2.6+ 行为一致——标准与规范的价值就在这类互通上。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 用 Spring 注解(@RestController)写 Quarkus | 注解不存在,编译错 | JAX-RS:@Path + @GET + @Produces |
| 在运行期大量用反射/Class.forName | Native 下不可达,直接失效 | 构建期注册或改用扩展提供的机制 |
| 拿 Native 对比「冷启动」就说 JVM 慢 | JVM 长跑峰值未必输 | 对比要区分冷启动与稳态吞吐 |
| 给 Quarkus 配 Spring 的 application.yml 语义 | 键前缀与自动配置不同 | 按 Quarkus 指南查对应配置键 |
| 把 quarkus:dev 当生产模式 | dev 模式带热重载与调试端口 | 生产用 package 产物 + 对应 profile |
卡点急救
quarkus:dev端口冲突:默认 8080 被占。改application.properties里quarkus.http.port=8081,或启动参数-Dquarkus.http.port=8081。- Native 构建失败提示缺少工具链:确认 GraalVM 已安装且
native-image可用(gu install native-image老版本;新版本自带);Windows 还需要 Visual Studio Build Tools 的 C++ 工具链。 - 改了代码没生效:dev 模式只监听
src/main下的标准位置;确认文件在正确目录、保存触发编译无报错(看终端日志 reload 输出)。
面试常问
- Native Image 为什么启动快?代价是什么? 可背版:构建期(AOT)就把应用与运行时编译成原生机器码,启动无需类加载、解释执行与 JIT 预热,内存中也没有 JIT 编译器本身,因此冷启动毫秒级、内存占用低。代价:构建慢、依赖封闭世界假设(反射/动态代理需构建期注册)、峰值优化弱于充分预热的 JIT。
- Quarkus 与 Spring Boot 怎么选? 团队与生态已标准化 Spring、服务长跑且无极致冷启动诉求 → Spring Boot;Serverless、K8s 大规模弹性缩容、边缘函数、内存成本敏感 → 评估 Quarkus(尤其 Native)。答法要点是「按场景匹配启动/内存/生态三个变量」,而不是站队。
- Quarkus 快的本质原因是什么? 构建期偏移:把 Bean 发现、代理生成、路由构建、配置解析从运行期搬到构建期,运行期只做最少装配;叠加 GraalVM Native 后连 JVM 引导都省了。一句话:把「运行时的智能」换成「构建期的确定性」。
自测三问
@Path+@GET属于哪个规范?Spring 的对应注解是什么?- 为什么说 Native 模式下「反射要用得小心」?封闭世界假设是什么意思?
- 同一个服务,什么指标变化时你会从 JVM 模式切到 Native?
小结一句话
Quarkus = 标准 CDI + JAX-RS + 构建期偏移 + 可选 Native;它是云原生 Java 的另一个答案,学它是为了看清 Spring 的成本结构。