[Servlet]JSP和Servlet的關係,以及JSP語法

JSP

JSP其實是一個Servlet,JSP頁面在JSP容器內運行。
第一次請求一個JSP頁面時,Servlet/JSP容器要做兩件事:

  1. 將JSP頁面轉換成一個JSP實現類,這是一個實現了javax.servlet.jsp.JspPage接口或其子接口javax.servlet.jsp.HttpjspPage的Java類,所生成的Servlet類名取決於Servlet容器。
  2. 如果轉換成功,Servlet/JSP會編譯Servlet類,之後,容器加載和實例化Java字節碼,並執行它對Servlet所做的生命週期操作。

對於同一個JSP頁面的後續請求,Servlet容器會查看這個JSP頁面從最後一次轉換以來是否修改過。如果修改過,那麼重新轉換和編譯執行。如果沒有救說明存在該JSP的Servlet了。第一次調用JSP頁面的時間總是比後續的更長,因爲需要轉換和編譯。對此可以採用以下優化措施:

  1. 配置應用程序,在啓動時調用所有JSP頁面
  2. 預先編譯JSP頁面,並將他們以Servlet的方式部署

JSP API 包含4個包:

  1. javax.servlet.jsp,包含核心類和接口,Servlet容器會用它們將JSP頁面轉換成Servlet。JspPage和HttpJspPage接口是這個包中的重要成員。所有頁面都必須實現JspPage或HttpJspPage。
  2. javax.servlet.jsp.tagext,包含用於開發定製標籤的類型。
  3. javax.el,提供Unified Expression Language API
  4. javax.servlet.jsp.el,提供Servlet容器必須支持的類,以便支持JSP的Expression Language

JSP頁面可以包含模版數據和句法元素,模版元素就是頁面內的html標籤和文本,而句法則是jsp頁面內獨有的語法元素。
比如一個簡單的index.jsp頁面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP compiled</title>
</head>
<body>Hello World
</body>
</html>

則會被轉換成叫index_jsp.java根據Servlet容器不同起名有差異

*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/8.0.30
 * Generated at: 2016-08-11 21:05:20 UTC
 * Note: The last modified time of this file was set to
 *       the last modified time of the source file after
 *       generation to assist with modification tracking.
 */
package org.apache.jsp;

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

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

  private static final javax.servlet.jsp.JspFactory _jspxFactory =
          javax.servlet.jsp.JspFactory.getDefaultFactory();

  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

  private static final java.util.Set<java.lang.String> _jspx_imports_packages;

  private static final java.util.Set<java.lang.String> _jspx_imports_classes;

  static {
    _jspx_imports_packages = new java.util.HashSet<>();
    _jspx_imports_packages.add("javax.servlet");
    _jspx_imports_packages.add("javax.servlet.http");
    _jspx_imports_packages.add("javax.servlet.jsp");
    _jspx_imports_classes = null;
  }

  private volatile javax.el.ExpressionFactory _el_expressionfactory;
  private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
    return _jspx_dependants;
  }

  public java.util.Set<java.lang.String> getPackageImports() {
    return _jspx_imports_packages;
  }

  public java.util.Set<java.lang.String> getClassImports() {
    return _jspx_imports_classes;
  }

  public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
    if (_el_expressionfactory == null) {
      synchronized (this) {
        if (_el_expressionfactory == null) {
          _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
        }
      }
    }
    return _el_expressionfactory;
  }

  public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
    if (_jsp_instancemanager == null) {
      synchronized (this) {
        if (_jsp_instancemanager == null) {
          _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
        }
      }
    }
    return _jsp_instancemanager;
  }

  public void _jspInit() {
  }

  public void _jspDestroy() {
  }

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

final java.lang.String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD");
return;
}

    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; charset=UTF-8");
      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("\n");
      out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
      out.write("<html>\n");
      out.write("<head>\n");
      out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
      out.write("<title>JSP compiled</title>\n");
      out.write("</head>\n");
      out.write("<body>Hello World\n");
      out.write("</body>\n");
      out.write("</html>\n");
    } catch (java.lang.Throwable t) {
      if (!(t instanceof javax.servlet.jsp.SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {
            if (response.isCommitted()) {
              out.flush();
            } else {
              out.clearBuffer();
            }
          } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

如果你想看到你自己部署的Web服務器上的JSP轉換出來的文件,可以到Tomcat的根目錄下work/Catalina/應用名/org/apache/jsp/下找到轉換後的java源代碼(請確保加載了該JSP頁面,可以瀏覽器訪問生成一下JSP)
所以我們就知道
JSP -> 轉換爲Java源碼.java文件 -> 編譯爲Java編譯後的.class文件
觀察生成的源碼我們知道頁面主體被轉換成一個_jspService方法,這個方法在HttpJspPage裏定義,並且通過HttpJspBaseservice方法實現調用。

隱式對象

Servlet容器會將及格對象給它運行的Servlet,例如HttpServletRequest和HttpServletResponse,並且在init方法中中獲得ServletConfig。
在JSP中可以通過使用九大隱式對象來獲得這些對象,如下表所示:

對象 類型
request javax.servlet.HttpServletRequest
response javax.servlet.HttpServletResponse
out javax.servlet.jsp.JspWriter
session javax.servlet.http.HttpSession
application javax.servlet.ServletContext
config javax.servlet.ServletConfig
pageContext java.servlet.jsp.PageContext
page javax.servlet.jsp.HttpJspPage
exception java.lang.Throwable

在編譯後的servlet類_jspService方法,就可以看到

    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;
    ....
    ....
    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;

比如在JSP代碼中可以這樣使用這些對象

<% 
    String userName =  request.getParameter("userName");
%>

pageContext

pageContext是指爲頁面創建的javax.servlet.jsp.PageContext。它提供了一些方法來獲得和Servlet有關的對象。比如getRequest, getResponse, getServletContext, getServletConfig以及 getSession。這些沒啥用,因爲都是可以通過隱式對象訪問到。
pageContext提供另外一些重要方法可以用來存取屬性,getAttributesetAttribute 方法等。
屬性可以保存在一下四個範圍內:page,request,session,application。其中page的範圍最窄,僅能在當前頁面能使用。還有請不要把隱式對象page當做是page域,而是指的隱式對象pageContext。request域指的是當前的ServletRequest,session域指的是HttpSession,application域指的是ServletCotext,簡單的說就是你能調用getAttributesetAttribute的對象。
什麼是當前頁面範圍呢,看_jspService方法中

finally {
    _jspxFactory.releasePageContext(_jspx_page_context);
}

最後釋放了pageContext資源。而我們的request 可能會因爲請求轉發而繼續存在,session本身就是記錄用戶信息的也能在一定時間內存在。application則是整個應用存在時間內有效。

jsp註釋風格

<%-- comment --%> jsp註釋不會被髮送到瀏覽器,不能嵌套

jsp指令

指令是第一種JSP元素,其指示JSP轉換器應該如何將某個JSP頁面轉換成Servlet命令。JSP2.2中定義了幾個指令,最重要的是這兩個:page 和 include。

page指令

利用page指令可以就當前JSP頁面的某些方面對JSP轉換器提出指示。例如告訴JSP轉換器隱式對象out應該有多大容量的緩存區,使用哪種內容類型。要導入那些Java類型。
語法如下:
<%@ page attribute1="value1" attribute2="value2"%>
page指令屬性如下:

  • import,指定導入一種或多種Java類型。就和java代碼中使用import類似。默認已經導入了java.lang, javax.servlet, javax.servlet.http, javax.servlet.jsp。
  • session,值爲true時,表示頁面參與Session管理,默認爲true。
  • buffer,表示out緩衝區的大小
  • autoFlush,值爲true時緩衝區自動刷新,false,必須顯示調用flush方法
  • isThreadSafe,默認爲false,servlet默認都是非線程安全的,如果爲true,就會加入線程同步
  • info,指定所生成Servlet的getServletInfo方法返回值
  • errorPage,表明出現錯誤時候的錯誤頁面
  • isErrorPage,該頁面是否負責處理錯誤
  • contentType,response返回內容類型
  • pageEncoding,頁面編碼方式
  • isELIgnored,是否忽略EL表達式
  • language,頁面使用的腳本語言,默認java,唯一有效值
  • extends,指定頁面生成的JSP必須繼承的類
  • deferredSyntaxAllowedAsLiteral,指明是否允許字符序列#{來獲取作爲該頁面和編譯的String字面值
  • trimDirectiveWhiteSpaces,是否從輸入內容中刪除只包含空格的模版文本,默認false。

page指令可以出現在任何地方,只有包含contentType和pageEncoding屬性時才需要放在模版數據前。

include指令

include指令可以將一個文件的內容放到當前JSP頁面中。
語法如下:
<%@ include file="url"%>
include方式有兩種,一種是<jsp:include page="url" flush="true"/>,該種稱爲動態include,動態include主要是對動態頁面的引入,它總是會檢查所引入的頁面的變化,如果所包含的資源在請求間發生變化,則下一次請求包含動作的jsp時,將包含資源的新內容。
另外一種是<%@ include file="url"%>,include指令在轉換時一次性地將內容複製到jsp中,如果所包含的資源發生變化,則使用include指令的jsp將不能反應出新的內容,除非重新編譯該jsp。
動態的引入時需要頻繁的變化和頁面信息的更新和交互,要佔用大量的資源開銷。降低頁面的訪問速度。如果在沒必要動態引入的情況下,不要使用動態include。

腳本元素

之前說過JSP中默認允許的腳本爲java,腳本元素將Java代碼合併成一個JSP頁面。
腳本元素有三種:

  • Scriptlet
  • 聲明
  • 表達式

Scriptlet

Java代碼塊,以<%開始,%>結束。

<%
    List<String> week = new ArrayList<>();
    week.add("Monday");
    week.add("Tuesday");
    week.add("Wednesday");
    week.add("Thursday");
    week.add("Friday");
    week.add("Saturday");
    week.add("Sunday");
%>
Hello World<br/>
<% 
    out.print("A weeks has:" + "<br/>");
    for(int i = 0; i < 7; i++) {
        out.print(week.get(i) + "<br/>");
    }
%>

然後經過轉換後生成的代碼如下所示如下,上述頁面中前面一個定義的Scriptlet變量後面是可見的。

    List<String> week = new ArrayList<>();
    week.add("Monday");
    week.add("Tuesday");
    week.add("Wednesday");
    week.add("Thursday");
    week.add("Friday");
    week.add("Saturday");
    week.add("Sunday");
//html template start
      out.write("\n");
      out.write("Hello World<br/>\n");
 //html template end
    out.print("A weeks has:" + "<br/>");
    for(int i = 0; i < 7; i++) {
        out.print(week.get(i) + "<br/>");
    }

表達式

表達式的運算結果會被填入隱式對象out的print方法中,以<%=開始, %>結束。
如下
Today is <%=java.util.Calendar.getInstance().getTime()%>
表達式後 不需要分號等同於下面這個Scriptlet:

Today is
<% 
out.print(java.util.Calendar.getInstance().getTime());
%>

聲明

可以聲明在JSP頁面中能夠使用的變量和方法,以<%!開始,%>結束。
比如

<%!
    List<String> week = new ArrayList<>();
%>
<%
    week.add("Monday");
    week.add("Tuesday");
    week.add("Wednesday");
    week.add("Thursday");
    week.add("Friday");
    week.add("Saturday");
    week.add("Sunday");
%>
Hello World<br/>
<% 
    showWeek(out);
%>
<%!
    public void showWeek(JspWriter out) throws IOException{
        out.print("A weeks has:" + "<br/>");
        for(int i = 0; i < 7; i++) {
            out.print(week.get(i) + "<br/>");
        }
    }
%>

還是和上個例子差不多,然後經過轉換後生成的代碼如下所示如下,可以發現在聲明區域裏的變量和方法都被統一移到生成代碼類的前端定義了。聲明可以在JSP任何位置定義,一個頁面可以有多個聲明。

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


    List<String> week = new ArrayList<>();


    public void showWeek(JspWriter out) throws IOException{
        out.print("A weeks has:" + "<br/>");
        for(int i = 0; i < 7; i++) {
            out.print(week.get(i) + "<br/>");
        }
    }

    ....
    ....

事實上現在開發很少用到腳本元素,大多是使用EL表達式和Tag,因爲這和HTML語法類似,便於管理。

動作

類似於Tag,動作提供以標籤樣式執行默寫操作。類似下面的格式:
<jsp:action property1="value1" property2="value2"/>

useBean

創建一個與某個Java類相關的變量,將表現邏輯和業務邏輯分隔開。事實上很少有了,因爲有了強大的EL表達式。

<html>
<head>
</head>
<body>
<jsp:useBean id="today" class="java.util.Date" />
<%=today%>
</body
</html

include

之前講過動態的包含另外一個資源,可以是JSP頁面,Servlet或者是靜態頁面。

forward

forward將當前頁面跳轉到另外一個資源,該跳轉是服務器這邊的跳轉。這裏要說到另外一個概念就是redirect客戶端方面的跳轉。
forward後,服務器會將請求派遣至對應的servlet,實際上客戶端那邊根本不知道進行了跳轉,地址欄不發生改變。
而redirect後,服務器返回30X狀態碼,告訴瀏覽器或者客戶端,你請求的資源已經移到這個新的位置,所以客戶端會跳轉到這個新的位置去,地址欄發生改變。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章