技术演进中的开发沉思-135 java-servlet篇: Web 的万物之源
我接触web系统开发,是在2002年。我2000毕业那年刚到科研所时,机房里还飘着 Windows 98 的开机音乐,Web 开发正处在 “刀耕火种” 的年代。那时要做个能显示实时科研数据的页面,得靠 CGI 程序 —— 就像每次有人寄信,都要临时雇个分拣员(启动进程),处理完就解雇,效率低得惊人。

之前做的都是windows或linux相关的程序,02年开始流行做web系统。这时开始对web系统有了认知,但基本上是从 Servlet开始的。Servlet 本质是驻留在服务器里的 “常驻分拣员”(Java 类),Web 服务器收到请求后不用新建进程,只需唤醒一个线程让 Servlet 处理。它就像邮局里固定工位的职员,既能接收信件(HttpServletRequest),又能封装回信(HttpServletResponse),还懂各种 “邮政规程”(HTTP 协议)。更关键的是,它带着 Java 的 “天生优势”—— 一次编译到处运行,这在当时 Windows 与 Unix 混战的服务器市场,简直是跨平台的 “万能钥匙”。
为什么说它是 Java Web 的起源?因为在它之前,Java 技术只能在桌面应用里打转。Servlet 第一次让 Java 真正走进了 Web 世界,就像为 Java 开辟了通往互联网的 “第一座桥梁”。后来的 JSP、Struts、Spring MVC,全都是架在这座桥之上的建筑。
二、代码示例:从 “手写 HTML” 到 “框架封装”
原始 Servlet(2002 年的电商订单处理)
// 当年写的电商订单查询Servlet,记得调试了三天
public class OrderQueryServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 1. 手动解析请求参数(像在信封上找收件人地址)
String orderId = req.getParameter("orderId");
// 2. 调用业务逻辑(那会儿还没有Spring,直接new对象)
OrderService service = new OrderService();
Order order = service.getOrderById(orderId);
// 3. 手动拼接HTML(最折磨人的步骤)
resp.getWriter().write("<html><body>");
resp.getWriter().write("<h3>订单号:" + order.getId() + "</h3>");
resp.getWriter().write("<p>金额:" + order.getAmount() + "</p>");
resp.getWriter().write("</body></html>");
}
}
Spring MVC 的 “现代继承者”
// 现在的订单控制器,本质是Servlet的“精装修版”
@RestController
public class OrderController {
@Autowired
private OrderService service;
// DispatcherServlet帮我们做了参数解析、请求路由
@GetMapping("/order/{id}")
public OrderDTO getOrder(@PathVariable String id) {
Order order = service.getOrderById(id);
return OrderConverter.toDTO(order); // 无需手动写HTML
}
}
三、实战场景:OA 系统里的技术抉择
2005 年我们给某国企做 内网系统系统,面临过一次关键抉择:用 Servlet+JSP 还是 PHP?
- Servlet 方案:我们用 Model2 模式(Servlet+JSP+JavaBean)实现公文流转。Servlet 做 “调度员”,接收用户的签批请求,调用 JavaBean 处理权限校验和流程更新,再转发到 JSP 显示公文页面。这种架构就像正规医院的分诊系统,各司其职,后来系统扩容到 500 人同时在线也没出问题。
- PHP 备选方案:当时试做了个公告模块,PHP 确实写得快,直接在 HTML 里插代码。但到了公文归档这种复杂场景,业务逻辑和页面代码搅在一起,就像厨房和餐厅混在一间屋,维护时找个 bug 都要翻几百行代码。
最终选择 Servlet 的核心原因:国企 系统 需要稳定运行十年以上,Servlet 的编译型特性和强类型检查,能减少后期 “带病运行” 的风险。这也是为什么早期企业级应用几乎都倒向了 Java 阵营。
四、对比分析:四种 Web 技术的 “江湖纷争”
|
技术 |
核心特点 |
像什么场景 |
电商 / OA 中的适配性 |
痛点 |
|
CGI |
进程级处理,语言无关 |
临时雇佣的快递员 |
小网站勉强可用,大促必崩 |
进程开销大,响应慢 |
|
ASP |
脚本嵌入 HTML,Windows 专属 |
家庭厨房,做饭吃饭一锅端 |
OA 轻量模块可用 |
跨平台差,维护混乱 |
|
PHP |
开源脚本,开发极速 |
快餐厅,出餐快但难办宴席 |
商品详情页等静态化场景 |
复杂业务逻辑难维护 |
|
Servlet |
线程级处理,Java 生态 |
连锁酒店后厨,标准化运作 |
订单、支付等核心模块 |
早期需手写 HTML |
印象最深的是 2003 年对比 CGI 和 Servlet 的性能:同样处理 1000 个订单查询,CGI 用了 47 秒,还占满了服务器内存;而 Servlet 只花了 8 秒,线程池就像待命的服务员,用完立刻归队待命。
五、常见陷阱:老程序员踩过的 “坑”
- 线程安全陷阱:2002 年做科研数据展示时,我在 Servlet 里定义了全局变量private int count = 0;,结果多用户访问时数据全乱了。后来才明白:Servlet 是单实例多线程,全局变量会被所有线程共享,就像多人共用一支笔,写出来的字肯定乱。
- 资源泄漏陷阱:早年写 OA 附件上传功能,忘记关闭InputStream,Tomcat 运行一周就报 “文件句柄耗尽”。现在想来,当时要是懂try-with-resources语法就好了,这也是 Servlet 开发从 “野蛮生长” 到 “规范成熟” 的缩影。
- 配置地狱陷阱:Servlet 2.5 时代,web.xml 里要写几十行映射配置,每次加新功能都要重启服务器。有次电商大促前改配置,手滑输错 URL,导致支付模块瘫痪了 10 分钟,至今想起来还冒冷汗。
六、最佳实践:穿越二十年的技术沉淀
基础规范:
- 永远不要在 Servlet 中定义非 final 的全局变量,用ThreadLocal存储线程私有数据
- 必须在finally块关闭流资源,Java 7 + 优先用try-with-resources
框架选型:
- 中小项目直接用 Spring Boot 内嵌 Tomcat,省去 Servlet 容器配置
- 大型微服务用 Spring Cloud Gateway,但要记得:底层的 DispatcherServlet 仍是 “总指挥”
云原生适配:
- 将 Servlet 应用打包成 Docker 镜像时,用 JDK 17 的 “瘦身模式”,镜像体积能从 GB 级降到 MB 级
- 高并发场景启用 Servlet 3.0 的异步处理:req.startAsync(),就像餐厅接了大单后先开单子,后厨慢慢做,前台继续接单
性能优化:
- 用 Redis 缓存 Servlet 的计算结果,比如电商商品详情页,避免重复查询数据库
- 对静态资源(JS/CSS)启用 Tomcat 的 Gzip 压缩,响应速度能提升 40%
七、现代价值:云原生时代的 “老当益壮”
今天当我按照自己的技术路线要写web这块,我打算从serlet开始写时,年轻同事问我:“都 2025 年了,还提 Servlet 干嘛?” 我笑着让他看 Spring Boot 的依赖包 ——spring-boot-starter-web里赫然躺着javax.servlet-api。
Servlet 在云原生时代的新价值,藏在三个地方:
- 容器化适配:Tomcat 容器能秒级启动,正好契合云原生 “弹性伸缩” 的需求。我们的 OA 系统在早高峰(9 点签到)自动扩容到 8 个实例,低谷时缩为 2 个,每月省了 30% 服务器费用
- 微服务基石:Spring Cloud 的服务通信、熔断降级,本质都是通过 Servlet 处理 HTTP 请求。就像高楼再花哨,地基还是钢筋混凝土
- Serverless 兼容:阿里云 Function Compute 支持把 Servlet 打包成函数,按调用次数计费。有个客户的电商秒杀接口,用这种方式把峰值成本压到了传统部署的 1/5
最后小结:
整理旧硬盘时,翻出 2002 年写的 Servlet 代码,注释里还画着歪歪扭扭的流程图。从当年在 Windows 2000 Server 上调试 Tomcat,到现在用 K8s 部署 Spring Boot 应用,Servlet 就像一位沉默的老伙计,见证了 Java Web 从蹒跚学步到驰骋云端。
它或许不再是舞台中央的主角,但所有现代 Java Web 框架的根,都深深扎在 Servlet 这片土壤里。就像我们这些老程序员,或许不再写底层代码,但那些踩过的坑、总结的经验,早已成了技术传承的一部分。
更多推荐
所有评论(0)