javax.servlet.Servlet 接口定義了 Servlet 的生命周期契約:
public interface Servlet {
void init(ServletConfig config) throws ServletException; // 初始化,僅一次
void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; // 服務,多次
void destroy(); // 銷毀,僅一次
ServletConfig getServletConfig(); // 獲取配置
String getServletInfo(); // 獲取描述信息
}
package com.focus;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyFirstServlet implements Servlet {
private transient ServletConfig config;
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
System.out.println("【MyFirstServlet】init() 執行 - 裝載到內存");
}
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
System.out.println("【MyFirstServlet】service() 執行 - 處理請求");
res.setContentType("text/html;charset=UTF-8");
PrintWriter out = res.getWriter();
out.println("Hello World from MyFirstServlet!
");
out.println("時間: " + new java.util.Date() + "
");
}
@Override
public void destroy() {
System.out.println("【MyFirstServlet】destroy() 執行 - 卸載銷毀");
}
@Override
public ServletConfig getServletConfig() { return config; }
@Override
public String getServletInfo() { return "MyFirstServlet v1.0"; }
}
webapps/MyServletDemo/
├── WEB-INF/
│ ├── web.xml
│ └── classes/
│ └── com/focus/MyFirstServlet.class
servlet-api.jar (位於 Tomcat/lib/):
javac -cp "D:/Tomcat/lib/servlet-api.jar" -d WEB-INF/classes src/com/focus/MyFirstServlet.java
web.xml 註冊與映射:
<servlet>
<servlet-name>MyFirstServlet</servlet-name>
<servlet-class>com.focus.MyFirstServlet</servlet-class>
<load-on-startup>1</load-on-startup> <!-- 啟動時加載 -->
</servlet>
<servlet-mapping>
<servlet-name>MyFirstServlet</servlet-name>
<url-pattern>/My</url-pattern>
</servlet-mapping>
啟動 Tomcat,瀏覽器訪問 https://localhost:8080/MyServletDemo/My,頁面顯示 "Hello World...",控制台輸出 init → service 日志。
GenericServlet 實現了 Servlet、ServletConfig、Serializable,提供了 init/destroy/getServletConfig 默認實現,僅需重寫 service()。
package com.focus;
import javax.servlet.*;
import java.io.*;
public class MyGenericServlet extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html;charset=UTF-8");
PrintWriter out = res.getWriter();
out.println("Hello from GenericServlet!
");
out.println("協議無關,適用於任何請求/響應模型
");
}
}
<servlet>
<servlet-name>MyGenericServlet</servlet-name>
<servlet-class>com.focus.MyGenericServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyGenericServlet</servlet-name>
<url-pattern>/Generic</url-pattern>
</servlet-mapping>
HttpServlet 繼承 GenericServlet,在 service() 中解析 HTTP 方法分發到 doGet/doPost/doPut/doDelete...,並封裝 HttpServletRequest/Response。
package com.focus;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyHttpServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("這是 doGet 響應
");
resp.getWriter().println("請用 POST 提交表單
");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
String username = req.getParameter("username");
PrintWriter out = resp.getWriter();
if (username == null || username.trim().isEmpty()) {
out.println("用戶名不能為空!
");
} else {
out.println("歡迎 " + username + " 光臨本站!
");
out.println("請求方法: " + req.getMethod() + "
");
out.println("客戶端 IP: " + req.getRemoteAddr() + "
");
}
}
}
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>登錄頁面</title></head>
<body>
<h2>用戶登錄</h2>
<form action="/MyServletDemo/MyHttpServlet" method="post">
<label>用戶名: <input type="text" name="username" required></label>
<button type="submit">提交</button>
</form>
</body>
</html>
<servlet>
<servlet-name>MyHttpServlet</servlet-name>
<servlet-class>com.focus.MyHttpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyHttpServlet</servlet-name>
<url-pattern>/MyHttpServlet</url-pattern>
</servlet-mapping>
/go.html → 顯示表單doPost → 顯示歡迎信息/MyHttpServlet → 觸發 doGet → 提示用 POST| 模式 | 語法 | 示例 | 匹配示例 |
|---|---|---|---|
| 精確匹配 | /path | /user/login | /user/login |
| 目錄匹配 | /path/* | /user/* | /user/, /user/list, /user/add/123 |
| 擴展名匹配 | *.ext | *.do, *.action | /login.do, /user/add.action |
| 默認 Servlet | / | / | 所有未匹配路徑 (靜態資源) |
<servlet>
<servlet-name>Demo</servlet-name>
<servlet-class>com.focus.MyHttpServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Demo</servlet-name>
<url-pattern>/demo.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Demo</servlet-name>
<url-pattern>/api/demo/*</url-pattern>
</servlet-mapping>
同一 Servlet 可映射多個 URL 模式。
/user/login)/user/* 勝過 /*)*.do)/)<servlet>
<servlet-name>InitServlet</servlet-name>
<servlet-class>com.focus.InitServlet</servlet-class>
<load-on-startup>1</load-on-startup> <!-- 數值越小越早載入 -->
</servlet>
<servlet>
<servlet-name>CacheServlet</servlet-name>
<servlet-class>com.focus.CacheServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
典型用途:初始化緩存、加載配置、啟動定時任務、預熱連接池。
訪問 https://localhost:8080/manager/html (需配置 tomcat-users.xml 添加 manager-gui 角色) 可:
| 維度 | 實現 Servlet 接口 | 繼承 GenericServlet | 繼承 HttpServlet (推薦) |
|---|---|---|---|
| 代碼量 | 最大 (5 方法全實現) | 中等 (僅 service) | 最小 (按需重寫 doXxx) |
| HTTP 特性支持 | 手動轉型 Request/Response | 手動轉型 | 原生 HttpServletRequest/Response |
| 方法分發 | 自己在 service 判斷 method | 自己判斷 | 自動分發 doGet/doPost... |
| 適用場景 | 教學理解契約、非 HTTP 協議 | 自定義協議、教學過渡 | 99% Web 應用開發 |
實戰中始終繼承 HttpServlet,按需重寫 doGet/doPost/doPut/doDelete,必要時重寫 init/destroy 管理資源。Servlet 3.0+ 更推薦使用 @WebServlet 註解替代 web.xml。