先把那些看上去差不多的部分聊一聊。两边都把虚拟线程当成标配能力了,但要用得上,需要在配置里把开关打开——SpringBoot 的配置项是
spring.threads.virtual.enabled=true,Solon 这边对应的是
solon.threads.virtual.enabled=true。路由里做版本控制,两家也都支持注解写法:SpringBoot 用 @RequestMapping(version),Solon 用 @Mapping(version)。声明式 HTTP 客户端也都有,风格稍有不同:SpringBoot 推的是 @HttpServiceClient,Solon 默认用 @NamiClient。再说云原生这块,GraalVM 优化、把应用直接打成镜像、缩短冷启动这类功能,两家都上了,但细节上还是有差别,工程化时得仔细调。
容器支持这件事上,分歧比较明显。SpringBoot 把官方支持的列表收窄到 Tomcat 11、Jetty 12.1,原先的 Undertow 支持被移掉了;Solon 的门槛低得多,像 JdkHttp、SmartHttp、Grizzly、VertX、Jetty(9 和 12.1)、Undertow(2.2 / 2.3)、Tomcat(9 / 11)这些都还能跑。什么意思?如果你的项目还在用老容器,或者常常在几种运行时之间切换,Solon 上手成本低,接入更灵活;要是你愿意对齐最新 Servlet 规范、把运行时统一起来,那 SpringBoot 的做法更干脆。
说回 Java 和规范这块。SpringBoot 把门槛往上提,要求最少 Java 17,官方还提议上到 Java 21,同时明确把 Jakarta EE 11、Servlet 6.1 当作硬性前提。好处是生态能往新标准靠拢,缺点是老项目得动真格去改包名、接口和一些行为。Solon 则走兼容路线,从 Java 8 一路支持上来,规范版本也更宽松,甚至可以不依赖某些规范基座,这对需要兼顾历史代码的团队友善得多。
数据绑定和空安全那块也值得当心。SpringBoot 做了个比较决断的选择:不再支持 Jackson 2.x,只支持 3.x;Solon 则保留了多版本兼容,团队可以按现有依赖选合适的 Jackson。别小看这个“数字”变更,3.x 在配置选项和某些序列化策略上和 2.x 有差别,测试用例很可能会把问题翻出来。两边都开始支持 JSpecify 风格的空安全注解,这对静态检查、IDE 提示有协助,但要落地还得团队配合代码检查工具。
迁移成本说清楚一点。把项目从 SpringBoot 2.x/3.x 拉到 4.0,不只是换个包名那么简单:Jakarta 包名替换会牵连到大量 import、第三方依赖;Servlet 行为在过滤器、异步请求处理上可能有细微差别,需要回归测试;还有 Jackson 升级,序列化细节、默认值策略可能改了,接口的兼容性测试不能省。Solon 3.7 的升级路径宽松,可在老版本的 Java 和组件上跑,适合分步迁移或者混合栈场景。换句话说,愿意做一次性大升级、追新标准的团队,SpringBoot 是合适的;需要稳妥过渡、兼容旧组件的团队,Solon 更省心。
再说几个开发时会碰到的实务点。Servlet 只承认 6.1,会影响到底层过滤器链、拦截器的语义,有些第三方中间件可能要做适配。Undertow 被 SpringBoot 移除,意味着依赖 Undertow 特性的项目迁过来得重新选容器或做适配。声明式客户端表面上都是注解式,但体验不一样:SpringBoot 的注解更贴合 Spring 的拦截器、错误处理链,而 Solon 的注解更轻量,调用语义更简洁。API 路由的版本控制目的一样,但具体的拦截器支持、自动生成文档的适配器细节会影响实际落地速度。
运维和镜像打包也不是儿戏。把应用直接打成 Docker 镜像对 CI/CD 很友善,两家都支持;但想拿 GraalVM 去做 native-image,就要面对反射配置、资源声明和兼容性问题,这不是一键能搞定的事,得专门写 native 构建脚本和反射配置。无服务器场景、微服务冷启动敏感的项目,Graal 优化能省出一笔运行成本,但投入也不小。
挑框架,像挑工具箱里的老虎钳。你想要标准化、统一、对齐最新规范,就得接受一次性较大的迁移成本;你想兼顾历史、慢慢过渡、在不同运行时里切换,就得选更弹性的方案。两边的发行说明和迁移文档都放在各自仓库里,兼容表和破坏性变更清单都有示例迁移脚本,可以按着做一步步检验。

