Web 開發中 Servlet 技術全面解析:生命周期、請求處理、線程安全與配置啟動
Servlet 是 Java Web 核心控制層組件,運行於 Servlet 容器(Tomcat/Jetty)中,處理 HTTP 請求/響應。本文系統梳理 Servlet 定義、生命周期、核心 API、線程安全、部署配置、字符編碼、錯誤頁面、監聽器/過濾器等實戰知識點。
1. Servlet 是什麼?
- 實現
javax.servlet.Servlet 接口(通常繼承 HttpServlet)的 Java 類。
- 由容器管理生命周期,處理客戶端請求,生成動態響應。
- MVC 模式中擔任 Controller(控制器):接收請求 → 調用 Service → 轉發/重定向到 View。
2. 生命周期三大方法
public class MyServlet extends HttpServlet {
@Override
public void init() throws ServletException {
// 1. 實例化後、首次請求前調用(僅一次)
// 適用:加載配置、初始化連接池、緩存預熱
System.out.println("init() 執行");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 2. 每次請求調用,分發到 doGet/doPost 等
// 可在此處理通用邏輯(編碼、日志、權限)
super.service(req, resp);
System.out.println("service() 執行");
}
@Override
public void destroy() {
// 3. 容器卸載 Servlet 前調用(僅一次)
// 適用:釋放資源、關閉連接池、保存狀態
System.out.println("destroy() 執行");
}
}
3. 定義 Servlet 必備步驟
- 類繼承
HttpServlet。
- 重寫
doGet/doPost(或 service)。
- 實現業務邏輯。
web.xml 配置映射(或 Servlet 3.0+ @WebServlet 註解)。
4. 請求轉發與重定向
// 轉發(服務端跳轉,URL 不變,共享 request)
RequestDispatcher rd = request.getRequestDispatcher("/target.jsp");
rd.forward(request, response);
// 重定向(客戶端跳轉,URL 變化,新 request)
response.sendRedirect("/target.jsp");
5. 作用域對象
| 作用域 | 獲取方式 | 生命周期 | 典型用途 |
| Request | request.setAttribute() | 一次請求 | 表單數據回顯、頁面間傳值 |
| Session | request.getSession().setAttribute() | 用戶會話(默認 30min) | 登錄狀態、購物車、用戶偏好 |
| Application | getServletContext().setAttribute() | 整個 Web 應用 | 全局配置、緩存數據、計數器 |
6. 線程安全核心問題
Servlet 默認單實例多線程:容器創建一個實例,每個請求分配一個線程調用 service()。
public class UnsafeServlet extends HttpServlet {
private String username; // 實例變量 = 共享變量!
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
username = req.getParameter("username"); // 線程 A 寫入 "Zhang"
try { Thread.sleep(6000); } catch (InterruptedException e) {}
resp.getWriter().write(username); // 線程 B 已改為 "Smith",A 也輸出 "Smith"!
}
}
解決方案
- 避免實例變量存儲請求狀態:使用局部變量(棧內存,線程私有)。
- 若必須共享:使用
ThreadLocal、同步塊、ConcurrentHashMap、或請求作用域 request.setAttribute()。
- 無狀態設計:Servlet 只做分發,業務邏輯委託給無狀態 Service Bean(Spring 默認單例,但無實例字段)。
7. web.xml 核心配置
<web-app>
<!-- 歡迎頁面 -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- Servlet 映射 -->
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.demo.MyServlet</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<load-on-startup>1</load-on-startup> <!-- 啟動時加載 -->
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/myservlet</url-pattern>
</servlet-mapping>
<!-- 錯誤頁面 -->
<error-page>
<error-code>500</error-code>
<location>/error/500.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.NullPointerException</exception-type>
<location>/error/500.jsp</location>
</error-page>
<!-- Session 超時(分鐘) -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<!-- 過濾器 -->
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.demo.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 監聽器 -->
<listener>
<listener-class>com.demo.MySessionListener</listener-class>
</listener>
</web-app>
8. 字符編碼處理
- POST 表單:
request.setCharacterEncoding("UTF-8")(必須在 getParameter() 前調用)。
- GET 參數:Tomcat 8+ 默認 UTF-8;舊版需
URIEncoding="UTF-8" 在 server.xml <Connector>。或手動轉碼:new String(param.getBytes("ISO-8859-1"), "UTF-8")。
- 響應輸出:
response.setContentType("text/html;charset=UTF-8") 或 response.setCharacterEncoding("UTF-8")。
- 轉發/重定向攜帶中文:重定向需
URLEncoder.encode(msg, "UTF-8"),接收端 URLDecoder.decode()。
9. 自動加載 Servlet (load-on-startup)
- 值為正整數,數值越小越早加載。
- 應用:啟動時預熱緩存(如加載字典表、初始化定時任務)。
- 配合
ServletContextListener 實現應用級初始化(更推薦)。
10. 過濾器與監聽器
- Filter:實現
Filter 接口,doFilter(req, resp, chain) 中調用 chain.doFilter() 放行。用途:編碼統一、登錄攔截、XSS 過濾、日志記錄、壓縮響應。
- Listener:實現
ServletContextListener, HttpSessionListener, ServletRequestListener 等。用途:應用啟動初始化、Session 統計在線人數、請求耗時監控。
總結
Servlet 雖被 Spring MVC 等框架封裝,但其生命周期、線程模型、作用域、配置機制是理解框架原理、排查疑難雜症(如線程安全 Bug、編碼亂碼、內存洩漏)的基石。掌握原生 Servlet,方能駕馭上層框架。
- 上一篇文章: Java IO 文件拷貝練習:遞歸遍歷、過濾 .java、重命名為 .txt
- 下一篇文章: Java 多態核心:Animal b = new Dog() 與 Dog c = new Dog() 的區別深度解析
Copyright ©
程式碼之家 All Rights Reserved