Solon 的服务器插件化:业务代码不动,底层容器随便换

2026-06-01 15 预计阅读时间:1 分钟
来源:oschina.net AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:9 分钟

Java Web 开发者大概都经历过这种场景——项目跑在 Tomcat 上,某天想换成 Undertow 试试性能,结果发现框架和容器之间有一堆隐式耦合,改完配置还要改代码。Solon 的做法干脆利落:把服务器做成插件,业务代码完全不动,换容器只换一个依赖。

内核 0.3MB,服务器全是插件

Solon 的内核只保留框架最基础的机制——IoC容器、生命周期、插件加载。HTTP 服务器不在内核里,而是以 solon.boot.xxx 插件的形式存在。目前官方提供了 10+ 种服务器插件:

插件依赖 底层服务器 适用场景
solon.boot.jlhttp JlHttp 超轻量嵌入、微服务侧车
solon.boot.jetty Jetty 中等负载、需JSP支持
solon.boot.undertow Undertow 高并发、Servlet兼容
solon.boot.tomcat Tomcat 传统Java Web、Servlet全特性
solon.boot.nettyhttp Netty 极高并发、非Servlet场景
solon.boot.smarthttp SmartHttp 轻量高性能
solon.boot.websocket 内置WebSocket WebSocket优先场景
solon.boot.smarthead SmartHead 轻量REST
solon.boot.rsocket RSocket 双向流通信

内核不依赖任何一个服务器实现。这意味着你的 Controller、Service、Filter 写完之后,底层跑的是 JlHttp 还是 Undertow,对业务代码完全透明。

业务代码与容器解耦的实际效果

先看一段最普通的 Solon 业务代码:

@Controller
public class UserController {

    @Inject
    private UserService userService;

    @Get
    @Mapping("/user/{id}")
    public User getUser(@Path long id) {
        return userService.findById(id);
    }

    @Post
    @Mapping("/user")
    public User createUser(@Body User user) {
        return userService.save(user);
    }
}

这段代码没有任何服务器相关的 import,没有 HttpServletRequest,没有 Servlet API。它只依赖 Solon 的通用注解。现在的问题是——它跑在哪个服务器上?

答案是:取决于你 pom.xml 里引入了哪个 boot 插件。

切换服务器:只改一行依赖

下面演示从 JlHttp(超轻量)切换到 Undertow(高并发)的完整过程。业务代码零改动。

第一步:用 JlHttp 启动(适合开发和小工具)

<!-- pom.xml -->
<dependencies>
    <!-- Solon 内核 -->
    <dependency>
        <groupId>org.noear</groupId>
        <artifactId>solon</artifactId>
        <version>3.0.5</version>
    </dependency>

    <!-- 轻量服务器插件 -->
    <dependency>
        <groupId>org.noear</groupId>
        <artifactId>solon.boot.jlhttp</artifactId>
        <version>3.0.5</version>
    </dependency>
</dependencies>

启动类:

public class App {
    public static void main(String[] args) {
        Solon.start(App.class, args);
    }
}

运行 mvn solon:run 或直接 java -jar,日志里会看到 JlHttp 启动信息,默认端口 8080。

第二步:切换到 Undertow(适合生产高并发)

只改依赖,不动任何 Java 代码:

<!-- pom.xml -->
<dependencies>
    <!-- Solon 内核(不变) -->
    <dependency>
        <groupId>org.noear</groupId>
        <artifactId>solon</artifactId>
        <version>3.0.5</version>
    </dependency>

    <!-- 换成 Undertow 服务器插件 -->
    <dependency>
        <groupId>org.noear</groupId>
        <artifactId>solon.boot.undertow</artifactId>
        <version>3.0.5</version>
    </dependency>
</dependencies>

再次启动,日志变成 Undertow 的启动信息。Controller、Service、Filter 全部照旧运行。

第三步:如果需要 Servlet 特性支持

Undertow 插件本身兼容 Servlet API,但如果你需要完整的 Servlet 规范(比如 JSP、Servlet Filter 链),再加一个 web 插件:

<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon.web.servlet</artifactId>
    <version>3.0.5</version>
</dependency>

插件加载机制:内核怎么找到服务器

Solon 启动时扫描 classpath 上所有 solon.boot.xxx 插件,通过 SPI 机制发现 SolonPlugin 接口的实现类。每个 boot 插件都实现了一个 start(SolonApp app) 方法,负责初始化自己的服务器并绑定到 Solon 的路由分发器上。

简化后的加载流程:

Solon.start()
  ├── 扫描 classpath 所有 SolonPlugin 实现
  ├── 按优先级排序boot 插件优先级最高
  ├── 依次调用 plugin.start(app)
       ├── solon.boot.undertow 插件初始化 Undertow Server
       ├──  HTTP 请求转发给 Solon 内核路由
       └── 监听端口开始接收请求
  └── 路由分发到 @Controller 方法

内核的路由分发器是统一的,不管哪个服务器插件接收请求,最终都走到同一个路由表。这就是解耦的关键——服务器插件只负责"接收请求并转交内核",不参与业务处理。

端口与配置:各插件通用

不管用哪个服务器插件,配置方式一致:

# app.yml - 通用配置,所有服务器插件都认
server:
  port: 8080
  host: 0.0.0.0

# Undertow 专属配置(其他插件会忽略不属于自己的项)
server.undertow:
  ioThreads: 8
  workerThreads: 256
  bufferSize: 8192

Solon 的配置体系也是插件化的——每个服务器插件只读取自己命名空间下的配置,互不干扰。你可以在同一个 app.yml 里为不同服务器写配置,切换插件后无需改配置文件。

选型建议与注意事项

实际选型时,几个关键判断维度:

  • 包体积敏感(CLI 工具、嵌入式场景):选 solon.boot.jlhttpsolon.boot.smarthttp,打包后整体可以控制在几 MB。
  • 需要 Servlet 兼容(迁移老项目、用 JSP):选 solon.boot.tomcatsolon.boot.undertow + solon.web.servlet
  • 纯 REST 高并发(微服务网关、API 服务):选 solon.boot.undertowsolon.boot.nettyhttp,非 Servlet 模式下吞吐更高。
  • WebSocket 为主(实时推送、IM):选 solon.boot.websocket 或 Netty 插件。

需要注意的边界:

  1. 不要混用多个 boot 插件——一个项目只引入一个 solon.boot.xxx,否则启动时会出现端口冲突或路由重复绑定。
  2. Servlet API 依赖要显式声明——如果你的业务代码用了 HttpServletRequest,必须搭配对应的 solon.web.servlet 插件,否则轻量服务器插件不提供 Servlet 对象。
  3. 静态文件处理——轻量插件(JlHttp、SmartHttp)内置了简单的静态文件服务,Undertow/Tomcat 则依赖 Servlet 容器的静态资源机制,行为有细微差异。

快速验证清单

换服务器插件后,跑一遍这个清单确认没有遗漏:

# 1. 确认只有一个 boot 插件依赖
grep "solon.boot" pom.xml | wc -l
# 期望输出: 1(或0,如果用父POM管理)

# 2. 启动后检查日志里的服务器标识
mvn solon:run 2>&1 | grep -i "server start"
# 应看到对应服务器名称(Undertow/JlHttp/Netty...)

# 3. 端口是否正确监听
curl http://localhost:8080/user/1
# 业务接口正常返回

# 4. 如果用了 Servlet API,确认 web 插件存在
grep "solon.web.servlet" pom.xml
# 有输出则 OK,无输出则检查代码是否依赖了 Servlet 对象

Solon 把服务器做成可插拔的依赖项,内核只管路由和业务调度。这种设计让"换服务器"从一项工程重构变成了改一行 Maven 依赖的事。对于需要在不同部署环境(开发轻量、生产高并发)之间灵活切换的团队,这个模式值得认真考虑。


相关推荐