承接第一部分,本文深入剖析 ServletConfig (單一 Servlet 配置) 與 ServletContext (全域應用上下文) 的進階用法:初始化參數、屬性共享、請求轉發、資源檔案讀取 (兩種方式)、客戶端快取控制。
<servlet>
<servlet-name>ConfigDemo</servlet-name>
<servlet-class>com.demo.ConfigDemoServlet</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>maxUpload</param-name>
<param-value>10485760</param-value>
</init-param>
</servlet>
@WebServlet(value = "/config", initParams = {
@WebInitParam(name = "encoding", value = "UTF-8"),
@WebInitParam(name = "maxUpload", value = "10485760")
})
public class ConfigDemoServlet extends HttpServlet { ... }
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config; // 保存引用
String encoding = config.getInitParameter("encoding");
Enumeration<String> names = config.getInitParameterNames();
while (names.hasMoreElements()) { ... }
}
<context-param> → 全 Servlet 可見。setAttribute/getAttribute/removeAttribute,跨 Servlet 共享資料 (快取、DataSource、配置物件)。getResourceAsStream、getRealPath、getRequestDispatcher。<context-param>
<param-name>jdbcUrl</param-name>
<param-value>jdbc:mysql://localhost:3306/mydb</param-value>
</context-param>
// 讀取
String url = getServletContext().getInitParameter("jdbcUrl");
// Servlet A: 初始化時載入配置放入 Context
public class InitServlet extends HttpServlet {
@Override public void init() {
AppConfig cfg = loadConfig();
getServletContext().setAttribute("appConfig", cfg);
}
}
// Servlet B: 業務中直接取用
public class BizServlet extends HttpServlet {
protected void doGet(...) {
AppConfig cfg = (AppConfig) getServletContext().getAttribute("appConfig");
// 使用 cfg...
}
}
// 在 Servlet A 中轉發給 Servlet B
RequestDispatcher rd = getServletContext().getRequestDispatcher("/servlet/B");
rd.forward(req, resp); // 瀏覽器地址列不變,請求物件同一個
| 維度 | ServletContext.getResourceAsStream | ClassLoader.getResourceAsStream |
|---|---|---|
| 路徑基準 | Web 應用根目錄 / (即 war 根) | Classpath 根目錄 (即 WEB-INF/classes) |
| 路徑寫法 | /WEB-INF/classes/config.properties/config.properties (WebRoot 下) | config.propertiescom/demo/config.properties |
| 適用場景 | Web 專屬資源、部署後可外部替換 | 類路徑資源、JAR 內資源、單元測試 |
| 大檔案支援 | ✅ 流式讀取,不載入記憶體 | ❌ 可能一次載入導致 OOM |
| 單元測試便利性 | ❌ 需容器環境 | ✅ 純 Java 環境即可 |
// 1. WEB-INF/classes 下
InputStream in = getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
// 2. WebRoot 目錄下 (部署後可直接替換)
InputStream in = getServletContext().getResourceAsStream("/config.properties");
// 3. 任意路徑 + getRealPath 取得絕對路徑 (謹慎用,戰場環境可能非爆炸 war)
String path = getServletContext().getRealPath("/WEB-INF/classes/large.dat");
try (InputStream in = new FileInputStream(path)) { ... }
ClassLoader cl = getClass().getClassLoader();
// classpath 根
InputStream in1 = cl.getResourceAsStream("db.properties");
// 套餐下
InputStream in2 = cl.getResourceAsStream("com/demo/db.properties");
Properties p = new Properties();
p.load(in1);
// 快取 1 天 (86400 秒)
resp.setDateHeader("Expires", System.currentTimeMillis() + 24*3600*1000L);
// 或 HTTP 1.1 Cache-Control
resp.setHeader("Cache-Control", "max-age=86400, public");
適用於靜態資源、不常變動的 JSON/API 回應,減少頻繁請求壓力。
ServletConfig 面向單一 Servlet 配置,ServletContext 面向整個 Web 應用。善用 Context 屬性共享單例資源 (DataSource、配置、快取)、用 getResourceAsStream 讀取部署後可熱替換的配置檔、用 forward 實現伺服器端流程控制,是 Servlet 進階開發的關鍵技能。