隱含物件


在之前的範例當中,曾經在 Scriptlet 中寫下 outrequest 等字眼,然後直接操作一些方法。像 outrequest 這樣的字眼,在轉譯為 Servlet 之後,會直接對應於 Servlet 中的某個物件,例如 request 就對應 HttpServletRequest 物件。像 outrequest 這樣的字眼,通常稱之為隱含物件(Implicit Object)或隱含變數(Implicit Variable)。

以下先列表對照 JSP 中的隱含物件與轉譯後的型態,有一些部份也許是你第一次看到的型態,將在稍後詳加說明:

  • out

    轉譯後對應 JspWriter 物件,其內部關聯一個 PrintWriter 物件。

  • request

    轉譯後對應 HttpServletRequest 物件。

  • response

    轉譯後對應 HttpServletResponse 物件。

  • config

    轉譯後對應 ServletConfig 物件。

  • application

    轉譯後對應 ServletContext 物件。

  • session

    轉譯後對應 HttpSession 物件。

  • pageContext

    轉譯後對應 PageContext 物件,它提供了 JSP 頁面資源的封裝,並可設定頁面範圍屬性。

  • exception

    轉譯後對應 Throwable 物件,代表由其他JSP 頁面丟出的例外物件,只會出現於 JSP 錯誤頁面(isErrorPage 設定為 true 的 JSP 頁面)。

  • page

    轉譯後對應 this

大部份的隱含物件,在轉譯後所對應的 Servlet 相關物件,先前講解 Servlet 的文件都有作過說明。page 隱含物件則是對應於轉譯後 Java 類別中的 this 物件,主要是讓不熟悉 Java 的網頁設計師,在必要時可以用較直覺的 page 名稱來存取。

exception 隱含物件將在之後談到 JSP 錯誤處理時時再加以說明。

至於 outpageContextexception 這些隱含物件,轉譯後的型態你可能是第一次看到,以下先針對這些隱含物件進行說明。

out 隱含物件不直接對應於先前說明 Servlet 時,由 HttpServletResponse 所取得的 PrintWriter 物件。out 隱含物件在轉譯之後,會對應於 javax.servlet.jsp.JspWriter 類別的實例,JspWriter 則直接繼承 java.io.Writer 類別。JspWriter 主要模擬了 BufferedWriterPrintWriter 的功能。

JspWriter 在內部也是使用 PrintWriter 來進行輸出,但 JspWriter 具有緩衝區功能。當使用 JspWriterprint()println() 進行回應輸出時,如果 JSP 頁面沒有緩衝,則直接建立 PrintWriter 來輸出回應,如果 JSP 頁面有作緩衝,則只有在出清(flush)緩衝區時,才會真正建立 PrintWriter 物件進行輸出。

會對頁面進行緩衝處理,表示在緩衝區滿的時候,可能有兩種處理方式:

  • 累積緩衝區的容量後再一次輸出回應,所以緩衝區滿了就直接出清。
  • 你也許是想控制輸出的量在緩衝區容量之內,所以緩衝區滿了表示有錯誤,此時要丟出例外。

在撰寫 JSP 頁面時,可以透過 page 指示元素的 buffer 屬性來設定緩衝區的大小,預設是 8kb。緩衝區滿了之後該採取哪種行為,則是由 autoFlush 屬性決定,預設是 true,表示滿了就直接出清。如果設定為 false,則要自行呼叫 JspWriterflush() 方法來出清緩衝區,如果緩衝區滿了卻還沒呼叫 flush() 資料 送出至客戶端,呼叫 println() 時將會丟出 IOException 例外。

接著說明 pageContext 隱含物件。pageContext 隱含物件轉譯後對應於 javax.servlet.jsp.PageContext 型態之物件,這個物件將所有 JSP 頁面的資訊封裝起來,轉譯後的 Servlet 可透過 PageContext 來取得所有的 JSP 頁面資訊。

例如在轉譯後的 Servlet 程式碼當中,要取得對應 JSP 頁面的 ServletContextServletConfigHttpSessionJspWriter 物件時,是透過以下的程式碼來取得:

application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();

所有的隱含物件都可以藉由 pageContext 來取得。除了封裝所有的 JSP 頁面資訊之外,還可以使用 pageContext 來設定頁面範圍屬性。在先前的文件中,你知道 Servlet 中可以設定屬性的物件有 HttpServletRequestHttpSessionServletContext,可分別用來設定請求範圍、會話範圍與應用程式範圍屬性。

在學到 JSP 時,你則會多認識一個用 pageContext 來設定的頁面範圍屬性,同樣是使用 setAttrubute()getAttribute()removeAttribute() 來進行設定。預設是可設定或取得頁面範圍屬性,頁面範圍屬性表示作用範圍僅限同一頁面之中。

來舉自行設定頁面範圍屬性的一個例子。你想要先檢查頁面範圍屬性中,是否曾被設定過某個屬性,如果有就直接取用,如果沒有就直接生成,且設定為頁面屬性。例如:

<%
    Some some = pageContext.getAttribute("some");
    if(some == null) {
        some = new Some();
        pageContext.setAttribute("some", some);
    }
%>

事實上,你可以透過 pageContext 設定四種範圍屬性,而不用使用個別的 pageContextrequestsessionapplication 來進行設定。以 pageContext 提供單一的 API 來管理屬性作用範圍,你可以使用以下的方法來進行設定:

getAttribute(String name, int scope)
setAttribute(String name, Object value, int scope)
removeAttribute(String name, int scope)

其中的 scope 可以使用以下的常數來進行指定:pageContext.PAGE_SCOPEpageContext.REQUEST_SCOPEpageContext.SESSION_SCOPEpageContext.APPLICATION_SCOPE,分別表示頁面、請求、會話與應用程式範圍。例如要設定會話範圍的屬性:

pageContext.setAttribute("login", "caterpillar", pageContext.SESSION_SCOPE);

要取得會話範圍的屬性時,可以使用以下的方式:

String attr = 
     (String) pageContext.getAttribute("login", pageContext.SESSION_SCOPE);

當不知道屬性的範圍時,也可以使用 pageContextfindAttribute() 方法來找出屬性,只要指定屬性名稱即可。findAttribute() 會依序從頁面、請求、會話、應用程式範圍尋找看看有無對應的屬性,先找到就傳回。例如:

Object attr = pageContext.findAttribute("attr");

隱含物件的認識,應視為認識 JSP 的一個過程,在 JSP 頁面中使用了隱藏物件,就代表著 JSP 頁面中多少都夾雜著 Scriptlet,在現在來說並不鼓勵,可以的話,在 JSP 頁面中不要使用隱藏物件。