考虑到现有 spring-boot 框架内部冗余越来越多, 所以一直想找比较轻量级的网络框架.
在多次从易用性和轻量化考虑之后发现了 Quarkus 号称 极致优化启动速度、内存占用 的框架.
官方网站: Quarkus
主要选择有以下优点:
大厂背书: RedHat 主导开发, 有大厂做代码质量把控
GraalVM: 同时支持 JVM 模式和 Native Image 模式, 可以将应用打包成原生二进制
功能完备: 大部分 spring-boot 相关都有对应替代, 按照文档将需求切换过去问题不大
这里先生成基础的 POM 依赖配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 <?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > io.meteorcat.game</groupId > <artifactId > pico</artifactId > <version > 1.0-SNAPSHOT</version > <properties > <maven.compiler.source > 21</maven.compiler.source > <maven.compiler.target > 21</maven.compiler.target > <maven.compiler.release > 21</maven.compiler.release > <compiler-plugin.version > 3.14.0</compiler-plugin.version > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <quarkus.platform.artifact-id > quarkus-bom</quarkus.platform.artifact-id > <quarkus.platform.group-id > io.quarkus.platform</quarkus.platform.group-id > <quarkus.platform.version > 3.27.0</quarkus.platform.version > <skipITs > true</skipITs > <surefire-plugin.version > 3.5.3</surefire-plugin.version > </properties > <dependencyManagement > <dependencies > <dependency > <groupId > ${quarkus.platform.group-id}</groupId > <artifactId > ${quarkus.platform.artifact-id}</artifactId > <version > ${quarkus.platform.version}</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement > <dependencies > <dependency > <groupId > io.quarkus</groupId > <artifactId > quarkus-core</artifactId > </dependency > <dependency > <groupId > io.quarkus</groupId > <artifactId > quarkus-arc</artifactId > </dependency > <dependency > <groupId > io.quarkus</groupId > <artifactId > quarkus-junit5</artifactId > <scope > test</scope > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > ${quarkus.platform.group-id}</groupId > <artifactId > quarkus-maven-plugin</artifactId > <version > ${quarkus.platform.version}</version > <extensions > true</extensions > <executions > <execution > <goals > <goal > build</goal > <goal > generate-code</goal > <goal > generate-code-tests</goal > <goal > native-image-agent</goal > </goals > </execution > </executions > </plugin > <plugin > <artifactId > maven-compiler-plugin</artifactId > <version > ${compiler-plugin.version}</version > <configuration > <parameters > true</parameters > <source > ${maven.compiler.release}</source > <target > ${maven.compiler.release}</target > <encoding > ${project.build.sourceEncoding}</encoding > </configuration > </plugin > <plugin > <artifactId > maven-surefire-plugin</artifactId > <version > ${surefire-plugin.version}</version > <configuration > <systemPropertyVariables > <java.util.logging.manager > org.jboss.logmanager.LogManager</java.util.logging.manager > <maven.home > ${maven.home}</maven.home > </systemPropertyVariables > </configuration > </plugin > <plugin > <artifactId > maven-failsafe-plugin</artifactId > <version > ${surefire-plugin.version}</version > <executions > <execution > <goals > <goal > integration-test</goal > <goal > verify</goal > </goals > </execution > </executions > <configuration > <systemPropertyVariables > <native.image.path > ${project.build.directory}/${project.build.finalName}-runner </native.image.path > <java.util.logging.manager > org.jboss.logmanager.LogManager</java.util.logging.manager > <maven.home > ${maven.home}</maven.home > </systemPropertyVariables > </configuration > </plugin > </plugins > </build > <profiles > <profile > <id > native</id > <activation > <property > <name > native</name > </property > </activation > <properties > <skipITs > false</skipITs > <quarkus.native.enabled > true</quarkus.native.enabled > </properties > </profile > </profiles > </project >
这就是基础的 POM 格式文件, 之后就是生成启动的唯一入口:
1 2 3 4 5 6 7 8 # 假设目前已经在项目目录之中, 则需要创建以下文件作为入口启动文件 # 这里以 Linux 为开发平台, touch 其实就是创建文件 touch src/main/java/io/meteorcat/game/PinoApplication.java # 启动文件取名一般是 '{artifactId}Application' 格式( artifactId 必须为大驼峰格式) # 最终启动文件路径为 'src/main/java/{groupId}/{}{artifactId}Application.java' # 我这里 groupId: io.meteorcat.game, artifactId: pion 所以最后路径如下 vim src/main/java/io/meteorcat/game/PionApplication.java # 注意目录内部如果还有其他带有 `main` 方法的文件就要删除掉
PinoApplication.java 文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package io.meteorcat.game;import io.quarkus.runtime.Quarkus;import io.quarkus.runtime.annotations.QuarkusMain;@QuarkusMain public class PinoApplication { public static void main (String[] args) { Quarkus.run(args); } }
之后之后就会输出以下最基础的信息, 该项目目前仅仅只有基础功能和容器功能(cdi):
1 2 3 2025-12-08 03:43:07,657 INFO [io.quarkus] (Quarkus Main Thread) pico 1.0-SNAPSHOT on JVM (powered by Quarkus 3.26.1) started in 0.453s. 2025-12-08 03:43:07,658 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated. 2025-12-08 03:43:07,659 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [cdi]
Web
spring-boot 主要是 Web 生态的开发简易性, 而 Quarkus 内部也能可以轻松实现, 这里先引入组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <dependencies > <dependency > <groupId > io.quarkus</groupId > <artifactId > quarkus-rest</artifactId > </dependency > <dependency > <groupId > io.rest-assured</groupId > <artifactId > rest-assured</artifactId > <scope > test</scope > </dependency > </dependencies >
1 2 # 创建并编辑对应外部访问控制器文件放置于 controller 目录之中 vim src/main/java/io/meteorcat/game/controller/HelloController.java
HelloController.java 文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package io.meteorcat.game.controller;import jakarta.ws.rs.GET;import jakarta.ws.rs.Path;import jakarta.ws.rs.Produces;import jakarta.ws.rs.core.MediaType;import org.slf4j.Logger;import org.slf4j.LoggerFactory;@Path("/hello") public class HelloController { final Logger logger = LoggerFactory.getLogger(HelloController.class); @GET @Produces(MediaType.TEXT_PLAIN) public String index () { logger.info("Hello World!" ); return "Hello World!" ; } }
启动之后访问默认地址显示如下:
1 2 3 4 5 6 7 8 # 默认 Java 的 Web 服务都是 localhost:8080 curl localhost:8080/hello # 如果要修改默认系统配置可以创建对应的配置文件 # 官方支持 yaml 和 properties 两种配置方式, 但是我个人更喜欢 properties 配置 echo '# 修改访问地址和端口 quarkus.http.host=127.0.0.1 quarkus.http.port=8889' > src/main/resources/application.properties
这里没问题应该会 curl 访问到我们输出的内容, 接下来就是我们日常用的比较多的 JSON 来做交换数据格式, 这里还需要组件支持:
1 2 3 4 5 6 7 8 9 10 <dependencies > <dependency > <groupId > io.quarkus</groupId > <artifactId > quarkus-rest-jackson</artifactId > </dependency > </dependencies >
之后我们需要创建两个组件用于格式化我们的统一JSON和拦截全局错误转化为我们自定义的格式:
ApiResponse 文件如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package io.meteorcat.game.utils;import jakarta.ws.rs.core.Response;import java.util.Collections;import java.util.Objects;public record ApiResponse <T>( int code, String message, T data ) { public static <T> Response response (int status, int code, String message, T data) { return Response .status(status) .entity(new ApiResponse <>(code, message, Objects.isNull(data) ? Collections.EMPTY_MAP : data)) .build(); } public static <T> Response response (int code, String message, T data) { return response(code, code, message, data); } public static <T> Response success (T data) { final Response.Status status = Response.Status.OK; return response(status.getStatusCode(), status.getReasonPhrase(), data); } public static Response fail (int code, String message) { return response(Response.Status.BAD_REQUEST.getStatusCode(), code, message, null ); } public static Response fail (int status, int code, String message) { return response(status, code, message, null ); } public static Response fail (String message) { int status = Response.Status.BAD_REQUEST.getStatusCode(); return response(status, status, message, null ); } }
ClientErrorExceptionMapper 文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package io.meteorcat.game.config;import io.meteorcat.game.utils.ApiResponse;import jakarta.ws.rs.ClientErrorException;import jakarta.ws.rs.core.Response;import jakarta.ws.rs.ext.ExceptionMapper;import jakarta.ws.rs.ext.Provider;@Provider public class ClientErrorExceptionMapper implements ExceptionMapper <ClientErrorException> { @Override public Response toResponse (ClientErrorException e) { Response response = e.getResponse(); return ApiResponse.fail( response.getStatus(), response.getStatusInfo().getReasonPhrase() ); } }
自定义的测试控制器文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package io.meteorcat.game.controller;import io.meteorcat.game.utils.ApiResponse;import jakarta.ws.rs.Consumes;import jakarta.ws.rs.GET;import jakarta.ws.rs.Path;import jakarta.ws.rs.Produces;import jakarta.ws.rs.core.MediaType;import jakarta.ws.rs.core.Response;import java.util.Map;@Path("/timestamp") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class TimestampController { @GET public Response index () { return ApiResponse.success(Map.of( "timestamp" , System.currentTimeMillis() )); } }
启动之后访问对应的请求地址就能查看到具体的 JSON 返回:
1 2 3 4 5 6 7 # 访问默认时间戳接口 - 返回 200 和 数据内容 curl http://127.0.0.1:8889/timestamp # {"code" :200,"message" :"OK" ,"data" :{"timestamp" :1765168872610}} # 访问不存在的接口 - 返回 404 curl http://127.0.0.1:8889/not-found # {"code" :404,"message" :"Not Found" ,"data" :{}}
注意: 错误拦截最好追加个全局通用的运行时崩溃拦截, 在崩溃的时候写入日志并且格式化成自定义 JSON 返回