【Java web】Servlet 详解
一、什么是 Servlet?—— 你不知道的 "网页服务员"
想象你走进一家网红书店(比如 "在线 Java 书店"),想买一本《Java 编程思想》。你告诉前台服务员你的需求,服务员去仓库找书、包装、收款,最后把书递给你 ——这个 "服务员" 的角色,在 Java Web 中就是 Servlet。
Servlet 本质:运行在服务器上的 Java 小程序,专门处理客户端(浏览器 / APP)的 HTTP 请求。当你在浏览器输入http://bookstore.com/books
时,就是 Servlet 在背后接收请求、查询数据库、生成动态网页内容并返回给你。
为什么需要 Servlet?
没有 Servlet 时,服务器只能返回静态 HTML 页面(内容固定)。有了 Servlet,才能实现 "用户登录后显示个性化书单"、"下单后修改库存" 等动态功能 —— 就像书店服务员能根据不同顾客的需求提供定制服务,而不是所有人都拿同一本样书。
二、Servlet 的 "一生"—— 从入职到退休
Servlet 的生命周期由Web 容器(比如 Tomcat,相当于书店的 "管理系统")全程管理,就像服务员的工作流程由书店规定一样,分为三个阶段:
1. 入职培训(初始化:init()
)
- 触发时机:第一次有顾客(请求)来时,或书店开门(容器启动)时(需配置
<load-on-startup>
)。 - 工作内容:加载 "工作手册"(初始化参数,如数据库连接信息)、准备 "工具"(创建数据库连接池)。
- 特点:一生只执行一次,类似服务员上班第一天的培训,之后直接上岗。
// Servlet初始化示例(类比服务员入职登记)
public class BookServlet extends HttpServlet {private String dbUrl; // 数据库地址(工作工具)@Overridepublic void init(ServletConfig config) throws ServletException {super.init(config);// 获取web.xml中配置的初始化参数(从管理系统拿工作手册)dbUrl = config.getInitParameter("dbUrl"); System.out.println("BookServlet初始化完成,数据库地址:" + dbUrl);}
}
2. 接待顾客(处理请求:service()
)
- 触发时机:每次有请求来时执行,比如用户查询 "Java 书籍"、"添加购物车"。
- 工作内容:
- 接收请求数据(顾客说 "我要 Java 书");
- 调用
doGet()
/doPost()
处理(根据请求类型分工,GET 查数据,POST 提交数据); - 返回响应(把书递给顾客)。
- 特点:多线程并发处理!容器会为每个请求分配一个线程,类似多个服务员同时接待不同顾客。
// 处理GET请求(类比顾客查询书籍)
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 设置响应类型为HTML(告诉顾客"这是书单")response.setContentType("text/html;charset=UTF-8");// 获取输出流(准备说话)PrintWriter out = response.getWriter();// 从数据库查询书籍(服务员去仓库找书)List<String> books = getBooksFromDB(); // 生成HTML响应(口头报书单)out.println("<h1>Java书籍列表</h1>");for (String book : books) {out.println("<p>" + book + "</p>");}
}// 模拟从数据库查询书籍
private List<String> getBooksFromDB() {List<String> books = new ArrayList<>();books.add("《Java编程思想》");books.add("《Effective Java》");return books;
}
3. 下班清理(销毁:destroy()
)
- 触发时机:书店打烊(容器关闭)或 Servlet 被移除时。
- 工作内容:释放资源,比如关闭数据库连接池、保存未完成的工作(如订单数据)。
- 特点:一生只执行一次,类似服务员下班前整理工具、交接工作。
@Override
public void destroy() {// 关闭数据库连接(下班前锁好仓库)closeDBConnection();System.out.println("BookServlet已销毁,资源已释放");
}
三、Servlet 如何 "接到订单"?—— 映射与访问
顾客(客户端)怎么找到对应的 Servlet?需要通过URL 映射,就像顾客根据 "文学区"、"科技区" 的指示牌找到对应的服务员。
1. 注解映射(推荐!简单快捷)
用@WebServlet
注解直接指定 URL,无需配置文件:
// 当用户访问 http://bookstore.com/books 时,由BookServlet处理
@WebServlet("/books")
public class BookServlet extends HttpServlet {// ...处理请求的代码...
}
2. web.xml 配置(不推荐!传统方式)
在web/WEB-INF/web.xml
中配置映射,适合复杂场景(如多个 URL 对应一个 Servlet):
<!-- 声明Servlet(注册服务员) -->
<servlet><servlet-name>BookServlet</servlet-name> <!-- 服务员姓名 --><servlet-class>com.bookstore.BookServlet</servlet-class> <!-- 服务员全类名 --><init-param> <!-- 初始化参数(工作手册) --><param-name>dbUrl</param-name><param-value>jdbc:mysql://localhost:3306/bookstore</param-value></init-param>
</servlet><!-- 映射URL(指定服务区域) -->
<servlet-mapping><servlet-name>BookServlet</servlet-name> <!-- 对应哪个服务员 --><url-pattern>/books</url-pattern> <!-- 访问路径 -->
</servlet-mapping>
四、核心 API:Servlet 的 "工具箱"
Servlet 有两个核心对象,就像服务员的 "记事本" 和 "对讲机",帮助处理请求和响应:
1. HttpServletRequest(请求对象)
- 作用:获取客户端请求信息,比如 URL 参数、表单数据、Cookie。
- 常用方法:
String getParameter(String name)
:获取表单参数(如request.getParameter("bookId")
获取书籍 ID);HttpSession getSession()
:获取会话对象(记录用户登录状态);RequestDispatcher getRequestDispatcher(String path)
:请求转发(让另一个 Servlet 协助处理)。
// 获取用户查询的书籍类型(比如用户输入"Java")
String category = request.getParameter("category");
if ("Java".equals(category)) {// 查询Java书籍...
}
2. HttpServletResponse(响应对象)
- 作用:设置响应信息,比如返回 HTML 内容、重定向页面、设置 Cookie。
- 常用方法:
PrintWriter getWriter()
:获取输出流,向客户端写内容;void sendRedirect(String location)
:重定向(让客户端跳转到新页面);void setContentType(String type)
:设置响应类型(如text/html
、application/json
)。
// 重定向到购物车页面(用户添加书籍后跳转到购物车)
response.sendRedirect("/cart");
五、Servlet 5.0 新变化:你需要知道的 "新规定"
如果你用 Spring Boot 3.0+,会发现 Servlet 的包名变了!这是因为 Java EE 改名为 Jakarta EE,就像 "中国移动" 改名为 "中国电信",服务没变,但 "公司名" 换了:
旧版本(Java EE) | 新版本(Jakarta EE 9+) |
---|---|
javax.servlet.* | jakarta.servlet.* |
javax.servlet.http.* | jakarta.servlet.http.* |
代码示例:
// 旧版本
import javax.servlet.http.HttpServlet;// 新版本(Spring Boot 3.0+)
import jakarta.servlet.http.HttpServlet;
六、小白避坑指南:Servlet 的 "那些坑"
1. 线程安全问题:别让服务员 "抢笔记"
问题:Servlet 是单实例多线程的,多个请求会同时调用service()
方法。如果用实例变量存请求数据,会导致线程安全问题(比如两个用户的数据互相覆盖)。
解决:用局部变量(每个线程独立)或加锁(synchronized
,但性能差,不推荐)。
public class BookServlet extends HttpServlet {// 错误:实例变量(所有线程共享,会冲突)private String currentUser; @Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) {// 正确:局部变量(每个线程独立)String currentUser = request.getParameter("username"); }
}
2. 忘记释放资源:服务员 "下班不锁门"
问题:init()
中创建的数据库连接、IO 流如果不在destroy()
中关闭,会导致资源泄漏(数据库连接耗尽)。
解决:在destroy()
中显式关闭资源。
3. 混淆请求转发和重定向
请求转发(forward ) | 重定向(redirect ) |
---|---|
服务器内部跳转(服务员内部交接) | 告诉客户端 "去新地址"(顾客重新排队) |
URL 不变(/books ) | URL 变为新地址(/cart ) |
共享request 域数据 | 不共享数据(需用 Session 或 URL 参数) |
示例:
// 请求转发:让BookDetailServlet处理详情页(内部协作)
request.getRequestDispatcher("/bookDetail").forward(request, response);// 重定向:添加书籍后跳转到购物车(让用户看到新页面)
response.sendRedirect("/cart");
七、Servlet 的 "继任者"?—— 为什么现在很少直接写 Servlet?
你可能听说 "现在都用 Spring MVC/ Spring Boot,不用 Servlet 了"—— 其实Spring MVC 的底层就是 Servlet!
Spring MVC 的DispatcherServlet
是一个 "总服务员",负责把请求分发给不同的 Controller(具体服务员)。直接写 Servlet 就像手动洗碗,用 Spring MVC 就像用洗碗机,效率更高,但原理相通。
学习建议:先掌握 Servlet,再学 Spring MVC,就像先学会手动挡,再开自动挡更轻松~
八、总结:Servlet 的核心价值
Servlet 是 Java Web 的基石,它的作用就像餐厅的服务员、医院的护士 —— 不起眼但关键。理解 Servlet 的生命周期、请求处理流程,能帮你搞懂任何 Java Web 框架的底层逻辑。
下一篇我们将学习JSP——Servlet 的 "模板兄弟",让动态网页开发更简单!