下面是一篇整理、润色并补充解释后的完整技术文档,目标是: 让第一次接触这套方案的读者,也能一步一步读懂“为什么要这样做、每一层在做什么、代码为什么这么写”。
Quarkus(无 HTTP)整合 tio-boot(有 HTTP)
同 JVM 架构完整方案说明
一、背景与问题动机
在 Java 服务架构中,常见的两类诉求往往是冲突的:
高性能、可控的网络层 希望完全掌控端口、协议、路由、线程模型(tio-boot 非常擅长)
成熟、强大的后台能力 希望使用 CDI、配置、生命周期管理、生态组件(Quarkus 非常擅长)
传统做法往往是:
- 要么只用 Quarkus(HTTP、容器、业务全绑在一起)
- 要么只用网络框架,自己造容器轮子
本方案的目标是: 在同一个 JVM 内,让 tio-boot 和 Quarkus 各司其职,互不干扰。
二、总体设计目标
1. tio-boot 的职责(唯一对外)
唯一对外的 HTTP / 网络服务
负责:
- 端口监听
- 路由
- handler
- 协议
不关心业务容器、依赖注入
2. Quarkus 的职责(后台容器)
不启动 HTTP
只作为:
- CDI 容器(Arc)
- Bean 生命周期管理
提供能力:
@ApplicationScoped@Inject
不感知 tio-boot 的存在
3. 业务代码的要求
- 业务只写在 Service 层
- 不依赖 tio-boot
- 不依赖 HTTP、Request、Response
三、整体架构关系图(逻辑)
HTTP 请求
↓
tio-boot handler
↓
Quarkus CDI Service
↓
业务逻辑 / 数据 / 外部资源
核心思想只有一句话:
tio-boot 负责“接请求”,Quarkus 负责“干活”
1. 依赖
<properties>
<compiler-plugin.version>3.12.1</compiler-plugin.version>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<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.8.6</quarkus.platform.version>
<skipITs>true</skipITs>
<surefire-plugin.version>3.2.5</surefire-plugin.version>
<!-- 版本属性 -->
<lombok-version>1.18.30</lombok-version>
<fastjson2.version>2.0.52</fastjson2.version>
<hotswap-classloader.version>1.2.6</hotswap-classloader.version>
<tio-boot.version>2.0.6</tio-boot.version>
<jfinal-aop.version>1.3.8</jfinal-aop.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-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<!-- Tio Boot 框架 -->
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>tio-boot</artifactId>
<version>${tio-boot.version}</version>
</dependency>
<!-- 热加载类加载器 -->
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>hotswap-classloader</artifactId>
<version>${hotswap-classloader.version}</version>
</dependency>
<!-- FastJSON2 用于 JSON 解析 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<!-- Lombok 用于简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok-version}</version>
<optional>true</optional>
<scope>provided</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>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</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.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
四、关键技术点说明
1. 为什么需要 QuarkusBeans 工具类
tio-boot 的 handler 不是 Quarkus 管理的对象, 因此不能直接 @Inject。
解决方式是: 在 handler 中,通过 Arc 容器“按需获取” Bean。
package com.litongjava.quarkus.config;
import io.quarkus.arc.Arc;
public final class QuarkusBeans {
private QuarkusBeans() {
}
public static <T> T get(Class<T> type) {
return Arc.container().instance(type).get();
}
}
这个类的角色非常明确:
- 是 tio 世界 → quarkus 世界的“桥”
- 不引入 HTTP、线程、协议耦合
- 只做一件事:从 CDI 容器拿 Bean
五、业务层(完全不感知 tio-boot)
MyService:标准 Quarkus Service
package com.litongjava.quarkus.service;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.HashMap;
import java.util.Map;
@ApplicationScoped
public class MyService {
public Map<String, Object> work() {
Map<String, Object> data = new HashMap<>();
data.put("from", "quarkus");
data.put("status", "ok");
data.put("time", System.currentTimeMillis());
return data;
}
}
设计要点:
- 使用
@ApplicationScoped - 无 HTTP 相关代码
- 无 tio 相关依赖
- 可被测试、可被替换、可迁移
六、Handler 层(tio-boot 世界)
HelloHandler:HTTP 与业务的“边界层”
package com.litongjava.quarkus.handler;
import com.litongjava.quarkus.service.MyService;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.server.handler.HttpRequestHandler;
import io.quarkus.arc.Unremovable;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
@Unremovable
public class HelloHandler implements HttpRequestHandler {
@Inject
MyService myService;
@Override
public HttpResponse handle(HttpRequest httpRequest) throws Exception {
return TioRequestContext.getResponse().setJson(myService.work());
}
}
关键说明(非常重要)
1. 为什么是 CDI Bean
- 让 Quarkus 管理生命周期
- 可以
@Inject MyService
2. 为什么必须加 @Unremovable
- Quarkus 会在构建期移除“看起来没被用到的 Bean”
- handler 是 被 tio-boot 在运行期使用的
- CDI 无法静态分析到这一点
- 所以必须明确告诉 Quarkus:这个 Bean 不能删
七、tio-boot 路由注册(桥接点)
WebHelloConfig:tio-boot 的配置入口
package com.litongjava.quarkus.config;
import com.litongjava.context.BootConfiguration;
import com.litongjava.quarkus.handler.HelloHandler;
import com.litongjava.tio.boot.server.TioBootServer;
import com.litongjava.tio.http.server.router.HttpRequestRouter;
public class WebHelloConfig implements BootConfiguration {
public void config() {
TioBootServer server = TioBootServer.me();
HttpRequestRouter requestRouter = server.getRequestRouter();
HelloHandler helloHandler = QuarkusBeans.get(HelloHandler.class);
requestRouter.add("/hello", helloHandler);
}
}
这里是整个架构最关键的连接点:
- tio-boot 在这里注册路由
- handler 实例来自 Quarkus CDI
- tio-boot 完全不知道 CDI 的存在
八、生命周期整合(Quarkus 启动 tio-boot)
TioBootLifecycle
package com.litongjava.quarkus.config;
import com.litongjava.context.Context;
import com.litongjava.tio.boot.TioApplication;
import io.quarkus.logging.Log;
import io.quarkus.runtime.ShutdownEvent;
import io.quarkus.runtime.StartupEvent;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
@ApplicationScoped
public class TioBootLifecycle {
private Context context;
void onStart(@Observes StartupEvent ev) {
long start = System.currentTimeMillis();
context = TioApplication.run(new WebHelloConfig());
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
}
void onStop(@Observes ShutdownEvent ev) {
if (context != null) {
Log.info("close");
context.close();
}
}
}
作用说明:
- Quarkus 启动完成 → 启动 tio-boot
- Quarkus 关闭 → 关闭 tio-boot
- 两者 同 JVM、同生命周期
九、程序入口
package com.litongjava.quarkus;
import io.quarkus.runtime.Quarkus;
import io.quarkus.runtime.annotations.QuarkusMain;
@QuarkusMain
public class MyApplication {
public static void main(String... args) {
Quarkus.run(args);
}
}
- 入口仍然是 Quarkus
- 但 HTTP 完全由 tio-boot 提供
十、运行效果
访问:
http://127.0.0.1/hello
返回:
{
"from": "quarkus",
"time": "1770044478509",
"status": "ok"
}
十一、总结(核心价值)
这套方案实现了:
职责彻底分离
- tio-boot:网络
- Quarkus:容器
零侵入业务代码
- 业务只依赖 CDI
同 JVM、无 RPC、无序列化开销
可演进
- 后续可接数据库、MQ、缓存
- handler 数量增加不影响架构
