技术演进中的开发沉思-148 java-servlet:servlet实例
现在回想,第一次写 Servlet 的经历,就像第一次独立做一道 “硬菜”:从准备食材(配置环境)、按步骤操作(编写代码)、调整火候(配置服务器),到最后端上桌(运行测试),每一步都藏着 “惊喜” 与 “惊吓”。这一章,咱们就回到那个敲代码要靠记事本、调试全靠 System.out 的年代,聊聊写第一个 Servlet 的那些事儿 —— 没有枯燥的代码堆砌,只有手把手学做菜的实在感。

当年刚懂了 Java Web 服务器的 “后厨秩序”,我就急着要 “露一手”。老同事劝我:“别急,先搞懂客人怎么点你的菜,再动手做。” 后来才明白,写 Servlet 不只是写代码,更要懂 “客人(浏览器)怎么找到你的菜(Servlet),后厨(服务器)怎么把菜端给客人”。就像开餐馆,先搞懂点单流程,再学做菜,不然客人喊破喉咙,你也不知道该给谁上菜。
一、 基本流程
第一次听说 “Servlet 调用流程” 时,我总觉得绕:浏览器发请求,HTTP 服务接请求,调用者 Servlet 找目标 Servlet,最后返回结果。直到老同事给我打了个比方,我才恍然大悟 —— 这就像餐馆里客人点 “招牌红烧鱼” 的流程:
- 客人点单:客人(浏览器)看着菜单(HTML 页面),点了 “招牌红烧鱼”,还特意说 “要现杀的”(带参数)—— 对应浏览器发 URL 请求:http://localhost:8080/servlet/Properties?test=1;
- 服务员认单:前台服务员(HTTP 服务)一看菜单,“招牌红烧鱼” 属于 “特色菜”,得交给专门的 “传菜师傅”(调用者 Servlet)—— 因为服务器配置了 “/servlet” 这个 “特色菜窗口”,只要 URL 里有它,就交给调用者 Servlet;
- 传菜师傅找厨师:传菜师傅(调用者 Servlet)看了下单子,知道这道菜得找 “张师傅”(Properties Servlet),如果张师傅还没上班(Servlet 未加载),就去叫他(加载 Servlet),让他先准备工具(init 初始化);
- 厨师做菜:张师傅(Properties Servlet)按客人要求(POST 请求),用新鲜的食材(请求参数)做好鱼(处理逻辑),装盘(生成 HTML);
- 端菜上桌:服务员把菜端给客人(HTTP 服务把 HTML 返回浏览器),客人尝了尝,点头说 “对味儿”(页面正常显示)。
- 当年我就是卡在 “服务员认单” 这步 —— 写了 URL 没配 “/servlet” 别名,结果浏览器一直报 404。后来翻第 2 章的笔记,才想起要在服务器管理工具里设 “servlet 别名”,就像餐馆没给 “特色菜” 标窗口,客人点了单,服务员不知道该找谁做。现在的 Spring Boot 虽然简化了配置,但底层的 “请求映射” 逻辑,还是当年这一套 —— 技术再变,底层的 “点单流程” 没变。
还有个细节我记了很久:Servlet 只加载一次,之后用多线程处理请求。就像餐馆里张师傅上班后,不会每次点鱼都重新叫他来,而是他一个人同时处理好几桌的鱼(当然,得保证食材够,对应线程安全)。当年我不懂线程安全,在 Servlet 里定义了全局变量存用户信息,结果多个人访问时数据串了 —— 就像张师傅把 A 桌的辣油加到了 B 桌的鱼里,客人当场就投诉了。后来才明白,Servlet 是 “单实例多线程”,全局变量要慎用,这是写 Servlet 的第一课,也是最重要的一课。
二、 在开始之前
现在的年轻程序员,打开 IDEA 就能写 Servlet,连环境变量都不用手动配。但当年我们写第一个 Servlet,光 “备料”(配置环境)就折腾了大半天 —— 就像学做菜前,得先买齐锅碗瓢盆、油盐酱醋,少一样都做不了。
那些年踩过的 “备料坑”
JDK 版本的 “选择困难”
当年手里有 JDK 1.1.7 和 1.2 两个版本,手册说 “建议不用 1.1.4 以前的”,我就选了 1.1.7。结果编译时总报 “找不到 javax.servlet 包”,后来才知道,JDK 本身不带 Servlet API—— 就像买了锅,却没买铲子,炒不了菜。后来从 JavaSoft 官网下载了 Servlet API 的 JAR 包,加到 CLASSPATH 里,才编译通过。现在想起来,当年的 CLASSPATH 配置真是 “玄学”,少个分号、多个空格都报错,每次都要打开 “我的电脑→属性→高级” 反复检查,像在找掉在地上的米粒。
服务器的 “兼容性陷阱”
一开始我用的是 Apache 服务器,以为能跑 Servlet,结果部署后没反应 —— 后来才知道,Apache 本身不支持 Servlet,得装 Servlet 引擎(比如 JServ)。就像买了烤箱,却不知道得先装烤盘,烤不了面包。最后换成 Java Web 服务器,才终于能跑 Servlet。现在的 Tomcat 已经把服务器和 Servlet 引擎集成好了,但当年那种 “凑齐一套工具” 的艰难,只有经历过的人才懂。
备料的 “正确姿势”
当年我总结的 “备料三步法”,现在看依然适用:
- 确认 “灶台”(JDK):装 1.1.7 及以上版本,配置 PATH(让系统找到 javac)和 CLASSPATH(让编译器找到 Servlet API);
- 准备 “调料”(Servlet API):把 servlet.jar 放到 CLASSPATH 里,就像做菜前把盐罐打开;
- 搭好 “后厨”(服务器):装 Java Web 服务器或 Tomcat,启动后能访问管理页面(比如localhost:9090),确认后厨 “通电可用”。
当年我把这三步写在便利贴上,贴在显示器旁边,每次写 Servlet 前都核对一遍 —— 就像厨师做菜前检查火、油、调料,少一步都不开工。现在虽然工具自动化了,但 “先确认环境可用” 的习惯,就是从那时候养成的。
三、Servlet 实例
当年选 Properties 作为第一个 Servlet,是因为它简单又实用:接收 POST 请求,返回客户信息(比如 IP、浏览器)和服务器属性(比如 JDK 版本、系统路径)。就像学做菜先学 “番茄炒蛋”,食材少、步骤明,做好了还能尝到 “成功的味道”。
3.1 编写 Servlet:按 “菜谱” 学做菜
编写 Servlet 的两步,当年我记成了 “学做菜的两步”:
学基础手法(继承 HttpServlet)
要写处理 HTTP 请求的 Servlet,必须继承javax.servlet.http.HttpServlet—— 就像学做番茄炒蛋,得先学 “热锅倒油” 的基础手法。当年我对着手册敲:public class Properties extends HttpServlet,心里还犯嘀咕:“为什么一定要继承这个类?” 后来才知道,HttpServlet 已经帮我们处理了 HTTP 协议的 “杂活”(比如解析请求头、判断 GET/POST),就像师傅已经帮你把番茄洗好切好,你只要炒就行。
练具体步骤(重写 doPost)
Properties 要处理 POST 请求,所以重写doPost方法。当年写代码时,最头疼的是记 API:比如resp.setContentType("text/html")是 “给菜定口味”(告诉浏览器返回的是 HTML),req.getRemoteHost()是 “问客人从哪来”(获取客户端 IP),out.println()是 “装盘上菜”(输出 HTML)。
印象最深的是遍历请求参数那段代码:
java.util.Enumeration enum = req.getParameterNames();
while (enum.hasMoreElements()) {
String name = (String) enum.nextElement();
out.println("<tr><td>Parameter '" + name + "'</td><td>" + req.getParameter(name) + "</td></tr>");
}
当年我不懂Enumeration是什么,只知道它能 “一个个取参数”,就像用勺子一勺勺舀出碗里的糖。后来学了集合框架,才知道这是早期的迭代器,现在都用Iterator了 —— 但那段代码,就像第一次成功打出的 “番茄块”,虽然不规整,却记得格外清楚。
还有init和destroy方法,当年我觉得 “这俩方法空着也没事”,就只写了super.init(cfg)和super.destroy()。后来做数据库项目时才明白,init里该初始化数据库连接(像师傅提前备好锅),destroy里该关闭连接(像收工后刷锅)—— 当年的 “敷衍”,后来都成了踩坑的教训。
3.2 配置服务器:给 “菜” 贴标签
写好代码只是 “做好了菜”,还得告诉后厨 “这道菜叫什么,放哪”—— 也就是配置服务器,让它找到你的 Servlet。
当年我犯了个低级错误:把Properties.class直接放在了/JavaWebServer/servlets目录下,结果服务器找不到。后来翻手册才知道,因为 Servlet 在javaservlets.samples包下,所以得建/servlets/javaservlets/samples目录,把 class 文件放进去 —— 就像把 “番茄炒蛋” 放在 “家常菜” 的柜子里,而不是随便扔在厨房台面上。
然后在服务器管理工具里配置 Servlet:
- Name:填 “properties”(给菜起个好记的名,不用和类名一样);
- Class Name:填 “javaservlets.samples.Properties”(告诉后厨菜的 “配方编号”);
- Load at Startup:选 “Enabled”(让师傅提前上班,不用等客人点单才来)。
配置完点击 “保存”,心里还忐忑:“这次能找到吗?”—— 就像第一次把做好的菜端到备餐区,怕服务员找不到。
3.3 编写调用 HTML:给客人 “画菜单”
要让客人(浏览器)点到这道菜,得写个 HTML 表单 —— 就像给客人画一张 “点菜单”,上面写清楚 “菜名”(ACTION 路径)和 “点单方式”(METHOD=POST)。
当年我写的 HTML 是这样的:
<form METHOD="POST" ACTION="/servlet/properties" ENCTYPE="x-www-form-encoded">
<center><INPUT NAME="Test" TYPE="submit" VALUE="测试Properties Servlet"></center>
</form>
这里又踩了个坑:ACTION 路径写成了 “/properties”,没加 “/servlet”—— 结果点击按钮后,浏览器报 404。后来才想起,服务器只认 “/servlet” 这个 “特色菜窗口”,就像客人点 “番茄炒蛋”,得说 “要家常菜区的番茄炒蛋”,不然服务员不知道去哪找。
把 HTML 文件放到/JavaWebServer/public_html目录(客人能看到的菜单区),打开浏览器输 “http://localhost:8080/test.html”,看到一个蓝色的按钮,心里满是期待 —— 就像餐馆刚贴出菜单,等着第一个客人点单。
3.4 看看它做得怎样:第一次 “上菜” 的激动
点击按钮的那一刻,我的手心都出汗了 —— 生怕又出什么错。几秒钟后,页面加载出来:上面是 “客户信息” 表格,有我的 IP、请求方法(POST),下面是 “服务器属性” 表格,有 JDK 版本、系统路径…… 那一刻,我盯着屏幕看了足足一分钟,甚至刷新了好几次,确认不是幻觉。
当年的 CRT 显示器有点模糊,但那些黑色的文字在我眼里格外清晰 —— 就像第一次看到自己做的菜被客人吃掉,还竖起大拇指。后来我把这个页面给老同事看,他笑着说:“不错,第一次做就能上桌,比我当年强。”
现在的 IDE 能一键运行 Servlet,再也不用手动配置、编译、部署,但那种 “从 0 到 1” 成功的激动,再也没有当年那么强烈了 —— 或许,技术的乐趣,不仅在于 “会做”,更在于 “第一次做对” 的成就感。
3.5 处理问题:做菜时的 “突发状况”
没有谁第一次写 Servlet 就一帆风顺,当年我遇到的 “突发状况”,比做菜时盐放多了还让人头疼。
逻辑错误:找 “掉在地上的勺子”
第一次运行时,页面只显示了客户信息,没显示服务器属性。我以为是代码错了,反复检查System.getProperties()那段,没发现问题。后来想起用System.out.println("进入服务器属性循环")调试,启动服务器后看控制台 —— 没看到这句话!原来我把服务器属性的代码写在了</table>后面,HTML 结构错了,浏览器没渲染出来。
当年没有调试器,全靠System.out“打日志”,就像做菜时找不到勺子,只能在厨房地上一点点找。后来学会用服务器的日志文件(比如 Java Web 服务器的 event.log),才不用盯着控制台看 —— 就像买了个手电筒,找东西方便多了。
异常处理:给客人 “道歉单”
有一次,我故意把 Servlet 类名写错,结果页面显示 “500 Internal Server Error”,还带个可爱的 Duke 小图标(Java 的吉祥物)。当年不知道这是服务器的默认错误页面,还以为是自己把服务器搞崩了,赶紧重启。后来才知道,用resp.sendError(HttpServletResponse.SC_NOT_FOUND)就能返回 404,告诉客人 “这道菜没有”—— 就像餐馆给客人递上 “抱歉,今日售罄” 的单子,比让客人干等强。
还有一次,数据库连接失败,Servlet 报UnavailableException,服务器提示 “该 Servlet 暂时不可用”。当年不懂 “暂时” 和 “永久” 的区别,后来才知道,加个超时时间(比如throw new UnavailableException(60)),服务器会 60 秒后再试 —— 就像师傅暂时没食材,告诉客人 “等 1 分钟再来”,而不是直接说 “做不了”。
3.6 Servlet 重新加载:“改进菜谱不用停业”
当年最让我惊喜的功能,是 Servlet 重新加载。我把Properties.class放在/servlets目录下,修改代码后重新编译,不用重启服务器,刷新页面就能看到新效果 —— 就像餐馆师傅改进了番茄炒蛋的做法,不用停业,直接给下一桌客人上新版的菜。
后来我把 Servlet 移到/classes目录,发现修改后不自动加载了。翻手册才知道,/servlets目录用的是 “自定义类加载器”,每次请求都会检查时间戳;/classes目录用的是 “标准类加载器”,加载后会缓存 —— 就像把食材放在 “保鲜柜”(/servlets)和 “冷冻柜”(/classes),保鲜柜里的食材能随时换,冷冻柜里的得解冻才能换。
这个发现让我明白:看似简单的 “重新加载”,背后是类加载器的逻辑 —— 技术的细节,往往藏在 “为什么这里行,那里不行” 的疑问里。
最后小结:
写第一个 Servlet,我花了整整两天:第一天配置环境、写代码,第二天调试、解决问题。虽然现在看来,这只是个简单的 “Hello World” 级别的程序,但它教会我的,远不止 “怎么写 Servlet”。
我学会了 “按规矩做事”:URL 要配别名、类要放对目录、HTML 路径要写对 —— 就像做菜要按步骤,先热锅再倒油,不然菜会糊。我也学会了 “解决问题的思路”:遇到错误先看日志,没思路就查手册,不要瞎猜 —— 就像做菜盐放多了,先尝味道,再加水或加菜,而不是直接倒掉。
现在的年轻程序员,可能觉得 Servlet “过时” 了,都用 Spring MVC、Spring Boot。但我总说,Servlet 是 Java Web 的 “地基”—— 就像学做菜要先学 “炒、煮、蒸”,再学复杂的菜式;学 Java Web 要先懂 Servlet,再用框架。框架再高级,底层还是 Servlet 在干活,就像再复杂的菜,还是离不开 “火候、调料” 这些基础。
下一章,咱们聊聊 “Servlet 链”—— 就像餐馆里多个师傅配合做一道 “满汉全席”,一个师傅切菜,一个师傅炒菜,一个师傅装盘,最后端出一道完整的菜。看看多个 Servlet 怎么分工协作,让 Java Web 的 “后厨” 效率更高。技术的路,就像学做菜:从第一道简单的菜开始,慢慢积累经验,遇到坑不退缩,最后才能做出 “拿手好菜”。第一个 Servlet,就是我 Java Web 路上的 “第一道拿手菜”,它的味道,我记了很多年。
更多推荐
所有评论(0)