第一個 JSP


相較於〈第一個 Servlet〉,要寫第一個 JSP 是簡單多了,只要一個副檔名為 .jsp 的檔案就可以了。例如以下是可以達到與第一個 Servlet 中相同功能的 JSP,若命名為 helloworld.jsp 的話:

<!DOCTYPE html>
<html>
    <head>
        <title>Hello! World!</title>
    </head>
    <body>
        <h1>Hello! World!</h1>
    </body>
</html>

看起來不就是個 HTML?別急,還沒有要到接觸動態的部份,將這個檔案部署到 Web 容器,用瀏覽器直接瀏覽網站存取 helloworld.jsp,當然!你會看到瀏覽器上顯示以上 HTML 標籤的結果。

雖然只用來輸出 HTML,但它確實是個 JSP,容器會在你第一次存取 JSP 檔頁時,將 JSP 轉譯為 .java,然後再編譯為 .class,之後載入容器進行處理並提供服務。舉例來說,如果你是使用 Tomcat 9,那麼上面這個 JSP 轉譯後的結果會如下所示:

package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class helloworld_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
                 org.apache.jasper.runtime.JspSourceImports {

  ...略

  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
      throws java.io.IOException, javax.servlet.ServletException {

    ...略

    final javax.servlet.jsp.PageContext pageContext;
    javax.servlet.http.HttpSession session = null;
    final javax.servlet.ServletContext application;
    final javax.servlet.ServletConfig config;
    javax.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    javax.servlet.jsp.JspWriter _jspx_out = null;
    javax.servlet.jsp.PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html");
      pageContext = _jspxFactory.getPageContext(this, request, response,
                null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("<!DOCTYPE html>\r\n");
      out.write("<html>\r\n");
      out.write("    <head>\r\n");
      out.write("        <title>Hello! World!</title>\r\n");
      out.write("    </head>\r\n");
      out.write("    <body>\r\n");
      out.write("        <h1>Hello! World!</h1>\r\n");
      out.write("    </body>\r\n");
      out.write("</html>");
    } catch (java.lang.Throwable t) {

      ...略

    } finally {

      ...略

    }
  }
}

其中 HttpJspBase 繼承自 HttpServlet,無疑地,JSP 最後也變成 Servlet 了,也正如你所看到的,你在 JSP 中寫的 HTML,是透過物件(out)來輸出字串,就如在第一個 Servlet 看到的範例。

若是輸出畫面,相較於寫 Servlet,寫 JSP 是簡單多了,因為 JSP 最後會轉譯為 Servlet,所以 Servlet 作的到的事,基本上 JSP 也都作的到,許多的書 會直接從 JSP 開始教學,因為簡單,不過,你了解 Servlet 越多,你 JSP 自然就越熟悉,例如,在上面的程式中,你看到 requestresponsesessionconfigout 等名稱,當你之後學 JSP 時就會知道,這些名稱正是 JSP 中所謂隱含物件(Implicit object),事實上它們在轉譯為 Servlet 之後就是個參數或區域變數。

使用 JSP,基本上無需任何設定,你存取的 URL 就指定了你要使用哪個 JSP 頁面,然後如果願意,你也可以 web.xml 中對 JSP 進行設定,一個應用的場合可能是設定初始參數。例如:

<?xml version="1.0" encoding="UTF-8"?>  
<web-app version="4.0" 
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"   
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee  
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">  
    <servlet>
        <servlet-name>HelloJSP</servlet-name>
        <jsp-file>/hello.jsp</jsp-file>
        <init-param>
            <param-name>defaultGuestName</param-name>
            <param-value>Guest</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloJSP</servlet-name>
        <url-pattern>/hello.jsp</url-pattern>
    </servlet-mapping>
</web-app>

在上例中,你在 <servlet> 中使用 <jsp-file> 指定實際上 HelloJSP 這個名稱的 Servlet,會是由 hello.jsp 來服務,並且該 Servlet 有初始參數 defalutGuestName 與值 Guest。你可以在 JSP 中取得這個它們。例如:

<!DOCTYPE html>
<html>
    <head>
        <title>Hello</title>
    </head>
    <body>
        <h1> Hello! <%= config.getInitParameter("defaultGuestName") %>!</h1>
    </body>
</html>

當你的瀏覽器觀看這個 JSP 時,畫面會顯示 Hello! Guest! 的結果。暫且不用擔心中間還有一些 API 細節,上面的例子只是在告訴你,必要時可以為 JSP 在 web.xml 中作些設定。

順便補充一下,最後這個例子若要可以運行,其 WAR 的結構要是:

/XXXXXXX.war/
            |hello.jsp
            /WEB-INF/
                    |web.xml