宣告、Scriptlet 與運算式元素


JSP 網頁會轉譯為 Servlet 類別,轉譯後的 Servlet 類別應該包括哪些類別成員、方法宣告或是哪些陳述句,在撰寫 JSP 時,可以使用宣告(Declaration)元素、Scriptlet 元素及運算式(Expression)元素來指定。

首先來看到宣告元素的語法:

<%! 類別成員宣告或方法宣告 %>

<%!%> 之間宣告的程式碼,都將轉譯為 Servlet 中的類別成員或方法,之所以稱為宣告元素,是指它用來宣告類別成員與方法。舉個例子來說,如果在 JSP 中撰寫以下的片段:

<%!
    String name = "caterpillar";
    String password = "123456";

    boolean checkUser(String name, String password) {
        return this.name.equals(name) && 
                 this.password.equals(password);    
    }
%>

則轉譯後的 Servlet 程式碼,將會有以下的內容:

package org.apache.jsp;
// 略...
public final class index_jsp     
                     extends org.apache.jasper.runtime.HttpJspBase
           implements org.apache.jasper.runtime.JspSourceDependent {
    String name = "caterpillar";
    String password = "123456";

    boolean checkUser(String name, String password) {
        return this.name.equals(name) && 
                 this.password.equals(password);
    }
    // 略….
}

所以使用 <%!%> 宣告變數時,必須小心資料共用與執行緒安全的問題。先前曾經談過,容器預設會使用同一個 Servlet 實例來服務不同使用者的請求,每個請求是一個執行緒,而 <%!%> 間宣告的變數對應至類別變數成員,因此會有執行緒共用存取的問題。

先前曾經提過,如果你有一些初始動作,想要在 JSP 載入時執行,則可以重新定義 jspInit() 方法,或是在 jspDestroy() 中定義結尾動作。定義 jspInit()jspDestroy() 的方法,就是在 <%!%> 之間進行,如此轉譯後的 Servlet 原始碼,就會有相對應的方法片段出現。例如:

<%!
    public void jspInit() {
        // 初始化動作
    }

    public void jspDestroy() {
        // 結尾動作
    }
%>

再來談到 Scriptlet 元素,先看看其語法:

<% Java陳述句 %>

注意到 <% 後沒有驚嘆號(!)。在宣告元素中可以撰寫 Java 陳述句,就如同你在 Java 的方法中撰寫陳述句一樣。事實上,<%%> 之間所包括的內容,將被轉譯為 Servlet 原始碼 _jspService() 方法中的內容。舉個例子來說:

<%
    String name = request.getParameter("name");
    String password = request.getParameter("password");
    if(checkUser(name, password)) {
%>
    <h1>登入成功</h1>
<%
    }
    else {
%>
    <h1>登入失敗</h1>
<%
    }
%>

這段 JSP 中的 Scriptlet,在轉譯為Servlet後,會有以下對應的原始碼:

package org.apache.jsp;
// 略...
public final class login_jsp 
    extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {
    // 略...
  public void _jspService(HttpServletRequest request, 
                               HttpServletResponse response)
        throws java.io.IOException, ServletException {
    // 略…
    String name = request.getParameter("name");
    String password = request.getParameter("password");
    if(checkUser(name, password)) {
        out.write("\n");
        out.write("    <h1>登入成功</h1>\n");
    }
    else {
        out.write("\n");
        out.write("    <h1>登入失敗</h1>\n");
    }
    // 略...
  }
}

直接 JSP 中撰寫的 HTML,都會變成 out 物件所輸出的內容。Scriptlet 出現的順序,也就是在轉譯為 Servlet 後,陳述句出現在 _jspService() 中的順序。

再來談到運算式元素,其語法如下:

<%= Java運算式 %>

你可以在運算式元素中撰寫 Java 運算式,運算式的運算結果將直接輸出為網頁的一部份。例如之前看過的範例中,有使用到一段運算式元素:

現在時間: <%= LocalTime.now() %>

注意!運算式元素中不用加上分號(;)。這個運算式元素在轉譯為 Servlet 之後,會在 _jspService() 中產生以下的陳述句:

out.print(LocalTime.now());

簡單地說,運算式元素中的運算式,會直接轉譯為 out 物件輸出時的指定內容(這也是為什麼運算式元素中不用加上分號的原因)。

下面這個範例綜合了以上的說明,實作了一個簡單的登入程式,當中使用了宣告元素、Scriptlet 元素與運算式元素。

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%!
    String name = "caterpillar";
    String password = "123456";

    boolean checkUser(String name, String password) {
        return this.name.equals(name) &&
                 this.password.equals(password);
    }
%>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>登入頁面</title>
    </head>
    <body>
<%
    String name = request.getParameter("name");
    String password = request.getParameter("password");
    if(checkUser(name, password)) {
%>
    <h1><%= name %> 登入成功</h1>
<%
    } else {
%>
    <h1>登入失敗</h1>
<%
    }
%>
    </body>
</html>  

如果請求參數驗證無誤就會顯示使用者名稱及登入成功的字樣,否則顯示登入失敗。

<%%> 用來在 JSP 中會用來作為一些元素的開頭與結尾符號,如果你要在 JSP 網頁中輸出 <% 符號或 %> 符號,不能直接寫下 <%%>,以免轉譯時被誤為是某個元素的起始或結尾符號。例如若 JSP 網頁中包括下面這段,就會發生錯誤:

<%
    out.println("JSP 中 Java 語法結束符號 %>");
%>

如果你要在 JSP 中輸出 <%%> 符號,要將角括號置換為替代字元。例如想輸出 <% 時可使用 &lt;%;而輸出 %> 時,可以使用 %&gt; 或使用 %\>。例如:

<%
    out.println("&lt;%與%\>被用來作為 JSP 中 Java 語法的部份");
%>

如果您想禁用 JSP 上的 Scriptlet,則可以在 web.xml 中設定:

<web-app …>
    …
    <jsp-config>
        <jsp-property-group>
            <url-pattern>*.jsp</url-pattern>
            <scripting-invalid>true</scripting-invalid>
        </jsp-property-group>
    </jsp-config>
</web-app>

會想禁用 Scriptlet 的情況,是在你不想讓 Java 程式碼與 HTML 標記混合的時候,一個網頁經由適當的規畫,切割商務邏輯與呈現邏輯的話,JSP網頁可以藉由標準標籤、EL 或 JSTL 自訂標籤等,徹底消除網頁上的 Scriptlet。