Servlet:Java Web 的历史兼容层,并非现代 Web 核心模型!
Servlet:并非为现代 Web 设计
很多人首次接触 Java Web 多从 Servlet 开始,自然而然认为它是 Java Web 的基础、所有框架的起点。然而,若回顾历史并将其置于如今的系统中审视,会发现一个反直觉的事实:Servlet 并非为当下的 Web 设计,更像是“旧时代 Web 服务器的扩展接口”,如今用它做的事已远超其当初设想。
Servlet 诞生时的 Web 状况
Servlet 诞生的年代,Web 本质上并非“应用平台”,更像远程文档系统。页面是核心,交互简单,即“提交表单,然后返回一个 HTML 页面”,那时没有前后端分离、API 网关、微服务以及复杂的分布式系统。当时要解决的问题很朴素,即如何在 Web Server 里嵌入一段 Java 代码,使其能动态生成页面,而非每次都 fork 一个 CGI 进程。Servlet 就在这样的背景下出现,它本质上不是框架,而是底层的运行在 Web 容器里的请求回调机制。
Servlet 简单的模型
Servlet 的核心是:来一个请求,调用一次方法,然后把结果写回去,即“void service(ServletRequest req, ServletResponse res)”。若去除所有框架包装,其思路一直是线程进来处理请求,线程出去,生命周期结束。这背后默认了一整套假设,如请求是短的、IO 是阻塞的、线程可以一一对应请求、系统不需要长期维持连接状态。在当时,这些假设没问题,但如今这些假设基本都变了。
现代 Web 已非“请求 - 响应”模式
如今的系统早已不是“点一下按钮,返回一个页面”的时代。请求可能只是入口,后面可能跟着 RPC、数据库、缓存、消息队列,一条链路可能运行很久。同时,WebSocket、SSE 这种长连接越来越常见,HTTP 不再只是短生命周期的协议。更关键的是,IO 成为核心成本,而非线程本身。但 Servlet 模型仍默认一个请求绑定一个线程,这导致现代系统的瓶颈在 IO,而 Servlet 的抽象中心却是线程的错位。
Servlet 异步未能解决问题
从 Servlet 3.0 开始支持异步,大致形式为“AsyncContext async = request.startAsync(); async.start(() -> { // do something async.complete(); });”。看似解决了线程阻塞问题,但实际上在复杂业务中,没人愿意这么写,原因并非 API,而是感觉不对。
异步未改变 Servlet 的“世界观”
Servlet 的异步本质上只是释放了线程,但整个请求模型未变。请求仍绑定在容器里,生命周期还是 request 驱动的,上下文还是依赖 ThreadLocal 的。它只是换了处理“在哪个线程上跑”的方式,未改变“这个东西本质上是请求驱动”的事实,所以解决的是局部问题,而非整体结构。
上下文被线程绑死的麻烦
在 Servlet 体系里,很多东西默认绑定在当前线程上,如请求上下文、安全上下文、事务、日志 trace,大多靠 ThreadLocal 传递。一旦进入异步流程,线程切换,这些上下文就会中断,要么手动传递,要么做各种包装。最终会发现,异步不是不能用,而是使用成本太高,让人犹豫是否使用。
Servlet 难入业务核心
Servlet 的异步更像是给容器自己用的能力,而非为业务开发设计的模型。它能优化吞吐、处理一些 IO 场景,但进入复杂业务编排,如 RPC + DB + MQ 这种组合,代码会迅速难以控制,让人怀疑是系统不适合异步,还是异步模型本身不适合业务。
Spring 未改变 Servlet 本质
很多人认为 Spring Boot 是现代 Web,但实际上它只是把 Servlet 包起来了。DispatcherServlet 还是 Servlet,Tomcat 还是那个模型。它做的是工程体验的优化,而非执行模型的重构。本质上,只是让 Servlet 变得“更好用”,但未脱离它。
Servlet:Java Web 的历史兼容层
若不考虑历史包袱,可以认为 Servlet 更像是 Java Web 的历史兼容层,而非现代 Web 的核心模型。它能用是因为生态庞大,而非它是最优解。
未来框架需重新设计执行模型
如今 Web 已改变,AI Native 时代又在进一步改变开发方式。若未来框架走向“对话驱动”或“运行时生成”,执行模型本身可能要重新设计。在那样的世界里,若被 Servlet 这一层绑定,很多事情将难以开展。正如所说,Servlet 不是为现代 Web 设计的,它只是现代 Java Web 不得不继续依赖的一段历史。
