Servlet 規範是 Java Web 開發的基石。理解 ServletConfig、ServletContext、GenericServlet、HttpServlet 之間的關係,以及 init()、service()、destroy() 的調用鏈路,對排查部署問題、設計濾鏡器鏈、實現框架擴展至關重要。
| 接口/類 | 職責 | 典型用法 |
|---|---|---|
javax.servlet.Servlet | 核心契約:定義 init/config/service/destroy/getServletInfo | 所有 Servlet 必須實現(直接或間接) |
GenericServlet | 协议无关抽象基类,实现 ServletConfig、Serializable,提供无参 init() 模板方法 | 自定义非 HTTP 协议 Servlet(如 FTP、SMTP) |
HttpServlet | HTTP 協議專用,重寫 service() 分發 doGet/doPost 等 | 99% Web 業務 Servlet 直接繼承 |
ServletConfig | 單個 Servlet 的初始化配置( | this.getInitParameter("key") |
ServletContext | 整個 Web 應用(一個 Context)的全局上下文,跨 Servlet 共享 | application 域屬性、資源訪問、初始化參數 |
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.demo.MyServlet</servlet-class>
<init-param>
<param-name>dbUrl</param-name>
<param-value>jdbc:mysql://localhost:3306/mydb</param-value>
</init-param>
</servlet>
獲取:this.getInitParameter("dbUrl") 或 getServletConfig().getInitParameter("dbUrl")。
<context-param>
<param-name>appName</param-name>
<param-value>MyWebApp</param-value>
</context-param>
獲取:getServletContext().getInitParameter("appName"),任意 Servlet/Filter/Listener 均可訪問。
容器啟動
│
├─> 實例化 Servlet(無參構造器)
│
├─> init(ServletConfig config) 【GenericServlet 實現】
│ │
│ ├─ 保存 config 引用
│ └─> init() 【無參,供子類重寫,此處執行業務初始化】
│
├─> 就緒服務請求
│ │
│ ├─> service(ServletRequest, ServletResponse) 【GenericServlet 抽象方法】
│ │ │
│ │ └─> HttpServlet.service() 【分發 doGet/doPost/doPut...】
│ │
│ └─> 多線程並發調用 service()
│
└─> 容器關閉/卸載
│
└─> destroy() 【釋放資源:關閉連接池、線程池、臨時文件】
doGet/doPost 失效,需自行分發 HTTP 方法。service() 多線程共享實例字段 → 嚴禁在 Servlet 類中定義非線程安全的實例變量(如 SimpleDateFormat、ArrayList、JDBC Connection)。請使用局部變量或 ThreadLocal。// 1. 域屬性共享(Application 範圍)
context.setAttribute("onlineCount", 100);
context.getAttribute("onlineCount");
// 2. 訪問 WEB-INF 下資源(不依賴絕對路徑)
InputStream is = context.getResourceAsStream("/WEB-INF/config.properties");
Properties p = new Properties();
p.load(is);
// 3. 獲取真實物理路徑(慎用,容器遷移可能失效)
String realPath = context.getRealPath("/uploads");
// 4. 請求轉發/包含(服務端跳轉)
context.getRequestDispatcher("/target.jsp").forward(req, resp);
// 5. 獲取 MIME 類型
String mime = context.getMimeType("image.png"); // image/png
// 6. 容器版本信息
String serverInfo = context.getServerInfo(); // Apache Tomcat/9.0.85
@WebServlet(
urlPatterns = "/api/user/*",
initParams = {
@WebInitParam(name = "pageSize", value = "20"),
@WebInitParam(name = "cacheEnable", value = "true")
},
loadOnStartup = 1,
asyncSupported = true
)
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
String pageSize = getInitParameter("pageSize"); // 直接獲取
// ...
}
}
架構建議: 現代 Spring Boot 項目中,極少直接編寫 Servlet。但理解其生命周期與上下文機制,有助於掌握
DispatcherServlet、FilterRegistrationBean、ServletListenerRegistrationBean等自動配置原理,以及排查「Bean 未注入」、「Filter 不生效」等容器級問題。