【Java web】Servlet 详解

发布于:2025-08-18 ⋅ 阅读:(11) ⋅ 点赞:(0)

一、什么是 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; // 数据库地址(工作工具)
    
    @Override
    public 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/htmlapplication/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; 
    
    @Override
    protected 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 的 "模板兄弟",让动态网页开发更简单!


网站公告

今日签到

点亮在社区的每一天
去签到