ServletContext
介面定義了 Servlet 運行的應用程式環境的一些行為與觀點,你可以使用 ServletContext
實作物件來取得所請求資源的 URL、設定與儲存屬性、應用程式初始參數,甚至動態設定 Servlet
實例。
ServletContext
本身的名稱令人困惑,因為它以 Servlet 名稱作為開頭,容易被誤認為僅是單一 Servlet 的代表物件。事實上,當整個 Web 應用程式載入 Web 容器之後,容器會生成一個 ServletContext
物件作為整個應用程式的代表,並設定給 ServletConfig
,你只要透過 ServletConfig
的 getServletContext()
方法就可以取得 ServletContext
物件。以下則先簡介幾個需要注意的方法:
getRequestDispatcher()
方法可以取得 RequestDispatcher
實例,使用時路徑的指定必須以 /
作為開頭,這個斜線代表應用程式環境根目錄(Context Root)。正如 調派請求 的說明,取得 RequestDispatcher
實例之後,就可以進行請求的轉發(Forward)或包含(Include)。
getServletContext().getRequestDispatcher("/pages/some.jsp").forward(request, response);
以 /
作為開頭有時稱為環境相對(Context-relative)路徑,沒有以 /
作為開頭則稱為請求相對(Request-relative)路徑。
實際上 HttpServletRequest
的 getRequestDispatcher()
方法在實作時,若是環境相對路徑,則直接委託給 ServletContext
的 getRequestDispatcher()
;若是請求相對路徑,則轉換為環境相對路徑,再委託給 ServletContext
的 getRequestDispatcher()
來取得 RequestDispatcher
。
如果想要知道 Web 應用程式的某個目錄中有哪些檔案,則可以使用 getResourcePaths()
方法。例如:
getServletContext().getResourcePaths("/")
.forEach(out::println);
使用時指定路徑必須以 /
作為開頭,表示相對於應用程式環境根目錄,傳回的路徑會像是:
/welcome.html
/catalog/
/catalog/index.html
/catalog/products.html
/customer/
/customer/login.jsp
/WEB-INF/
/WEB-INF/web.xml
/WEB-INF/classes/com.acme.OrderServlet.class
可以看到,這個方法會連同 WEB-INF 的資訊都列出來。如果是個目錄資訊,則會以 /
作結尾。以下這個範例利用了 getResourcePaths()
方法,自動取得 avatars 目錄下的圖片路徑,並透過 <img>
標籤來顯示圖片:
package cc.openhome;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@WebServlet(
urlPatterns = {"/avatar"},
initParams = {
@WebInitParam(name = "AVATAR_DIR", value = "/avatars")
}
)
public class Avatar extends HttpServlet {
private String AVATAR_DIR;
@Override
public void init() throws ServletException {
super.init();
AVATAR_DIR = getInitParameter("AVATAR_DIR");
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<body>");
getServletContext().getResourcePaths(AVATAR_DIR)
.forEach(avatar -> {
out.printf("<img src='%s'>%n", avatar.replaceFirst("/", ""));
});
out.println("</body>");
out.println("</html>");
}
}
如果想在 Web 應用程式中讀取某個檔案的內容,則可以使用 getResourceAsStream()
方法,使用時指定路徑必須以 /
作為開頭,表示相對於應用程式環境根目錄,或者相對是 /WEB-INF/lib 中 JAR 檔案裏 META-INF/resources 的路徑(Web應用程式中,JAR檔案必須放在 /WEB-INF/lib 中,這是規定),執行結果會傳回 InputStream
實例,接著你就可以運用它來讀取檔案內容。底下是個讀取 PDF 並傳送給客戶端的範例:
package cc.openhome;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.*;
@WebServlet(urlPatterns={"/ebook"},
initParams={
@WebInitParam(name="PDF_FILE", value="/WEB-INF/jdbc.pdf")
}
)
public class Ebook extends HttpServlet {
private String PDF_FILE;
@Override
public void init() throws ServletException {
super.init();
PDF_FILE = getInitParameter("PDF_FILE");
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String coupon = request.getParameter("coupon");
if("123456".equals(coupon)) {
response.setContentType("application/pdf");
try(InputStream in = getServletContext().getResourceAsStream(PDF_FILE)) {
OutputStream out = response.getOutputStream();
byte[] buffer = new byte[1024];
int length = -1;
while((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}
}
}
}
}
正如在〈ServletConfig〉介紹過的,每個 Servlet 都會有一個相對應的 ServletConfig
物件,你可以透過 ServletConfig
的 getInitParameter()
方法來讀取初始參數,而每個 Web 應用程式都會有一個相對應的 ServletContext
,針對「應用程式」初始化時所需用到的一些參數資料,你可以在 web.xm l中設定應用程式初始參數,設定時使用 <context-param>
標籤來定義。例如:
<web-app ...>
<context-param>
<param-name>MESSAGE</param-name>
<param-value>/WEB-INF/messages.txt</param-value>
</context-param>
…
</web-app>
每一對初始參數要使用一個 <context-param>
來定義。之後在程式中可以透過 ServletContext
的 getInitParameter()
方法來讀取初始參數。因此 Web 應用程式初始參數常被稱為 ServletContext
初始參數。
在整個 Web 應用程式生命週期,Servlet 所需共用的資料,則可以設定為 ServletContext
屬性。由於 ServletContext
在 Web 應用程式存活期間都會一直存在,所以設定為 ServletContext
屬性的資料,除非你主動移除,否則也是一直存活於 Web 應用程式之中。
可以透過 ServletContext
的 setAttribute()
方法設定物件為 ServletContext
屬性,之後可透過 ServletContext
的 getAttribute()
方法取出該屬性。若要移除屬性,則透過 ServletContext
的 removeAttribute()
方法。
Web 容器會提供每個 ServletContext
一個暫存目錄,這個目錄的資訊可以藉由 ServletContext
的 getAttribute()
方法取得 "javax.servlet.context.tempdir"
屬性來得知,這個常數值可以透過 ServletContext.TEMPDIR
來取得。
注意!ServletContext
並非執行緒安全,所以必要時必須注意屬性設定時共用存取的問題。