当我们访问购物网站时,常常会看到形如 http://xx.com/brandNo=1 的 URL 请求,服务器接收这类请求后,向浏览器返回的大多是 JSON 格式的数据。这个过程看似简单,背后却隐藏着一套成熟的 Web 处理机制
早期 Web 开发中,每个请求的处理通常包含三个基本步骤:接收请求、处理请求和响应请求。其中,接收和响应这两个环节的逻辑高度一致,属于通用功能,不具备业务差异性。于是,工程师们将这部分公共流程抽象出来,封装成独立的“Web 服务器”,负责统一完成请求的解析与响应的返回
而处理请求的部分则因业务逻辑不同而变化多样,因此被抽取为 Servlet,交由开发人员自行编写具体实现
随着架构的演进,三层架构(表现层、业务逻辑层、数据访问层)逐渐普及,原先集中在 Servlet 中的业务逻辑被进一步拆分,转由 Service 层和 Dao 层分别承担,Servlet 逐渐退化为纯粹的控制层组件
由于 Servlet 本身不擅长动态生成 HTML 页面,JavaServer Pages(JSP)技术应运而生,用于更方便地实现页面渲染。而随着 Spring 生态的成熟,SpringMVC 逐渐取代了传统 Servlet 在 Web 层的主导地位,提供了更强大、更便捷的请求映射、参数解析和视图解析机制
在学习 Java Web 技术的过程中,很多开发者会经历这样的心理变化:从 Servlet,到 Filter、Listener,我们逐渐发现自己写的类没有 main 方法,也没有显式 new 实例,只是简单地在 web.xml 中配置标签,它们就“自动”运行了起来。这种开发模式背后,其实是“注入”和“回调”机制在发挥作用
越深入编程,我们越会意识到:我们能做的事情其实非常有限。主流框架已经完成了大部分底层和架构性工作。我们只需实现某个接口、添加某个注解,框架便会自动创建实例,并通过依赖注入将组件装配到合适的位置。最终,在它预设的执行流程中,我们的代码被自动调用
这种开发模式可以用一个古老的成语来形象地概括:“闭门造车,出门合辙”。我们只需关注业务实现,而框架确保这些实现能够嵌入到更大的运行流程中
很多时候,框架如同一位傀儡师,而我们编写的代码则像是傀儡——我们负责为傀儡设计外观与装饰,真正的调度和运作,则完全由背后的框架(傀儡师)掌控
尽管如今直接编写 Servlet 的机会变少,但理解其设计思想与演进历程,仍然对我们把握 Web 开发本质具有深刻意义
Servlet 版本进化
Servlet 技术的演进几乎是 Java Web 技术发展的缩影
梳理各个主要版本的进化历程、核心特性和其背后的意义
1. Servlet 初期:开创时代
-
版本: Servlet 1.0 (1997)
-
核心特性:
- 定义了最基本的
Servlet
接口 (init
,service
,destroy
) 和ServletRequest
/ServletResponse
对象 - 提供了处理 Web 请求的基本框架
- 定义了最基本的
-
意义与局限:
- 意义: 首次将 Java 应用于服务器端动态网页技术,统一了 HTTP 请求/响应的处理模型,奠定了 Java Web 技术的基石
- 局限: 功能非常原始,所有逻辑都在
service
方法中完成,需要手动解析请求参数、组装响应,开发效率极低。配置复杂
Servlet 组件运行在 Servlet 容器(Container)中,通过与容器交互,就可以响应一个 HTTP 请求
2. Servlet 2.x:走向成熟与 JSP 的黄金组合
- 代表性版本: Servlet 2.3 (2001) & Servlet 2.4 (2003)
- 核心进化:
- Filter(过滤器) (2.3引入): 这是一个革命性的特性。允许对请求和响应进行预处理和后处理,实现了横切关注点(如:日志记录、安全验证、编码设置、性能审计)的模块化,与业务逻辑(Servlet)解耦
- 监听器(Listener) (2.3引入): 可以监听 Web 应用生命周期中的事件(如:应用启动、关闭、Session 的创建和销毁),为应用初始化、资源加载等提供了hook(钩子)
- War包标准: 明确了 Web 应用打包和部署的标准格式
- XML配置支持: 使用
web.xml
作为标准的部署描述符,配置更加规范
- 意义:
- MVC 雏形: Servlet + JSP + JavaBean 的组合成为了早期 Java Web 开发的事实标准,即 Model 1 和 Model 2(MVC)架构
- 职责分离: Filter 的引入使得公共逻辑得以抽取,Servlet 可以更专注于核心业务处理
- 生态系统形成: 基于这套稳定的标准,大量的第三方框架(如 Struts、Spring)开始涌现
3. Servlet 2.5:注解的曙光
- 版本: Servlet 2.5 (2005)
- 核心进化:
- 要求运行在 Java 5 及以上平台
- 支持注解(Annotation),最主要的是
@WebServlet
,@WebFilter
,@WebListener
- 意义:
- 简化配置: 开始减少对
web.xml
的依赖,开发者可以直接在 Servlet 类上使用@WebServlet("/url")
进行配置,开发体验大幅提升。这是向“约定优于配置”理念迈出的重要一步
- 简化配置: 开始减少对
4. Servlet 3.0:异步革命与全面可编程配置
- 版本: Servlet 3.0 (2009)
- 核心进化:
- 异步处理: 提供了
AsyncContext
接口,允许 Servlet 线程在处理耗时操作(如数据库查询、调用外部API)时,将请求挂起并释放容器线程,待耗时操作完成后再恢复响应。这极大地提升了容器对高并发请求的处理能力,是应对现代 Web 应用复杂性的关键特性 - 可编程式配置: 允许通过代码(实现
ServletContainerInitializer
接口)在应用启动时动态添加 Servlet、Filter 和 Listener,彻底告别web.xml
。这是 Spring MVC 等现代框架无缝集成的基础 - 模块化部署: 支持 Web 片段(web-fragment.xml),允许库(JAR 包)自带 Web 配置,实现了插拔式的组件化。
- 安全增强: 提供了更强大的身份验证和授权机制
- 异步处理: 提供了
- 意义:
- 为现代框架铺平道路: Spring 等框架利用可编程配置,实现了“零配置”启动和强大的自动化配置
- 支持高性能应用: 异步处理为开发实时应用、Comet 风格应用以及后来的 WebSocket 打下了基础
5. Servlet 3.1:非阻塞 I/O 的补充
- 版本: Servlet 3.1 (2013)
- 核心进化:
- 非阻塞 I/O: 引入了
ReadListener
和WriteListener
接口。与 3.0 的异步处理结合,允许在数据读写这种 I/O 操作本身也不阻塞线程,进一步榨干性能,适用于文件上传/下载、大数据流处理等场景 - 协议升级处理: 支持处理 HTTP 协议升级请求(例如升级到 WebSocket)
- 非阻塞 I/O: 引入了
- 意义:
- 将异步支持从请求处理层面扩展到了数据读写层面,形成了完整的异步非阻塞解决方案
6. Servlet 4.0:拥抱 HTTP/2
- 版本: Servlet 4.0 (2017)
- 核心进化:
- 基于 HTTP/2: 强制要求容器支持 HTTP/2 协议,带来了服务器推送(Server Push)、多路复用(Multiplexing)、头部压缩等现代化特性
- 服务器推送API: 提供了
PushBuilder
API,让 Servlet 可以方便地向客户端推送资源
- 意义:
- 让 Java Web 应用能够充分利用 HTTP/2 的性能优势,提升页面加载速度和用户体验。这是对底层网络协议演进的一次重要跟进
7. Servlet 5.0
- Servlet 5.0 (2020): 主要是为了将 Java EE 改名为 Jakarta EE 而更新的版本。所有
javax.servlet
包名全部更名为jakarta.servlet
。这是一个重要的品牌和法律变更,技术功能上没有变化
8. Servlet 6.0
Servlet 6.0(随 Jakarta EE 10 发布)主要包括以下改进
https://jakarta.ee/zh/specifications/servlet/6.0/
- URI 路径的解码与规范化澄清 改进路径安全处理,明确何时如何归一化 URI,使路径解析更加一致、安全
- Cookie API 全面更新
- 更新 Cookie 类及相关文档,从使用指定早期标准(FC 2109)迁移到 RFC 6265
- 增加对
SameSite
属性等新属性的支持,改进 Session Cookie 和普通 Cookie 的属性支持
- 新增唯一标识 API 添加入方法,用于获取当前请求或关联连接的唯一标识符,便于更精细的追踪与诊断
- 模块化支持增强
提供
module-info.java
,支持在 Java Platform Module System(JPMS)模块化环境中使用 Servlet API,更符合 Jakarta EE 10 的方向 - 废除 X-Powered-By 建议
删除容器默认添加
X-Powered-By
HTTP 头的推荐,提升安全性 - 异步与请求/响应行为澄清
- 明确
getRemoteAddress()
、setCharacterEncoding(null)
等方法在不同场景下的行为 - 优化
ServletInputStream.isReady()
和ServletOutputStream.isReady()
的文档说明,解释调度或阻塞行为
- 明确
- 容器包装行为调整
放宽对请求与响应包装(wrapping)的严格要求,允许容器以更灵活的方式实现
RequestDispatcher
功能 - 对动态添加的监听器限制放宽
去除程序化添加
ServletContextListener
等监听器时对某些ServletContext
方法的使用限制,提高动态配置弹性 - 与 JSP Pages 协同更新
在
JspPropertyGroupDescriptor
中新增getErrorOnELNotFound()
方法,以配合 Jakarta Pages 3.1 的变更 - 废弃与移除旧 API
- 弃用
doHead
中响应包装(wrapped response),鼓励容器本身处理 HEAD 请求行为 - 移除长期弃用的 API,如
SingleThreadModel
、HttpSessionContext
、HttpUtils
以及若干已弃用的方法
- 弃用
- Java SE 最低版本要求提高 Servlet 6.0 需要至少 Java SE 11 或更高版本
总结与进化脉络
版本 | 发布年份 | 核心特性 | 意义 |
---|---|---|---|
1.0 | 1997 | 基本接口 | 奠定基础,开创Java Web时代 |
2.3 | 2001 | Filter, Listener | 实现模块化,形成MVC雏形,生态繁荣 |
2.5 | 2005 | 注解支持 (@WebServlet ) |
开始简化配置,提升开发效率 |
3.0 | 2009 | 异步处理,可编程配置 | 为现代框架(Spring Boot)奠基,支持高并发 |
3.1 | 2013 | 非阻塞I/O | 完善异步性能,支持流式处理 |
4.0 | 2017 | HTTP/2支持 | 拥抱现代网络协议,提升应用性能 |
5.0 | 2020 | javax -> jakarta | 品牌和法律变更,迈向新时代 |
进化主脉络
- 功能增强:从处理基本请求,到增加过滤器、监听器等模块化功能
- 配置简化:从繁冗的
web.xml
到方便的注解,再到完全可编程的配置 - 性能提升:从同步阻塞模型,到异步处理,再到非阻塞 I/O,不断适应高并发、实时性的需求
- 协议跟进:持续支持最新的网络协议标准,如 HTTP/2
- 角色转变:从台前的主力(MVC中的Controller),逐渐退居幕后,成为底层基石,被 SpringMVC 等更上层的框架所封装和增强,但其核心思想始终是Java Web技术的绝对核心