1. 程式人生 > >JavaWeb學習篇之----Jsp詳解

JavaWeb學習篇之----Jsp詳解

今天我們來看一下Jsp的相關知識,首先來看看一下Jsp的相關定義:

簡介:

JSP全稱是JavaServer Pages,它和servle技術一樣,都是SUN公司定義的一種用於開發動態web資源的技術。
JSP這門技術的最大的特點在於,寫jsp就像在寫html,但:
它相比html而言,html只能為使用者提供靜態資料,而Jsp技術允許在頁面中巢狀java程式碼,為使用者提供動態資料。
相比servlet而言,servlet很難對資料進行排版,而jsp除了可以用java程式碼產生動態資料的同時,也很容易對資料進行排版。

不管是JSP還是Servlet,雖然都可以用於開發動態web資源。但由於這2門技術各自的特點,在長期的軟體實踐中,人們逐漸把servlet作為web應用中的控制器元件來使用,而把JSP技術作為資料顯示模板來使用。

其原因為,程式的資料通常要美化後再輸出:
讓jsp既用java程式碼產生動態資料,又做美化會導致頁面難以維護。
讓servlet既產生資料,又在裡面巢狀html程式碼美化資料,同樣也會導致程式可讀性差,難以維護。
因此最好的辦法就是根據這兩門技術的特點,讓它們各自負責各的,servlet只負責響應請求產生資料,並把資料通過轉發技術帶給jsp,資料的顯示jsp來做。

Jsp的執行原理:

目標:
Web伺服器是如何呼叫並執行一個jsp頁面的?
Jsp頁面中的html排版標籤是如何被髮送到客戶端的?
Jsp頁面中的java程式碼伺服器是如何執行的?
Web伺服器在呼叫jsp時,會給jsp提供一些什麼java物件?

思考:JSP為什麼可以像servlet一樣,也可以叫做動態web資源的開發技術?

其實Jsp就是一個Servlet,所以我們要先介紹Servlet的相關技術,當我們第一次訪問Jsp的時候,Jsp引擎都會將這個Jsp翻譯成一個Servlet,這個檔案存放在Tomcat中的work目錄中,這裡,我們新建一個MyJsp.jsp頁面,然後訪問以下,我們看一下翻譯後的原始碼:

以下是MyJsp.jsp頁面的內容:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    
    <title>My JSP 'MyJsp.jsp' starting page</title>
    
  </head>
  
  <body>
    This is my JSP page. <br>
  </body>
</html>

下面是翻譯之後的原始碼:

package org.apache.jsp;

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

public final class MyJsp_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {

  private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();

  private static java.util.List _jspx_dependants;

  private javax.el.ExpressionFactory _el_expressionfactory;
  private org.apache.AnnotationProcessor _jsp_annotationprocessor;

  public Object getDependants() {
    return _jspx_dependants;
  }

  public void _jspInit() {
    _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
    _jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
  }

  public void _jspDestroy() {
  }

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

    PageContext pageContext = null;
    HttpSession session = null;
    ServletContext application = null;
    ServletConfig config = null;
    JspWriter out = null;
    Object page = this;
    JspWriter _jspx_out = null;
    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("\r\n");
      out.write("\r\n");
      out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");
      out.write("<html>\r\n");
      out.write("  <head>\r\n");
      out.write("    \r\n");
      out.write("    <title>My JSP 'MyJsp.jsp' starting page</title>\r\n");
      out.write("    \r\n");
      out.write("  </head>\r\n");
      out.write("  \r\n");
      out.write("  <body>\r\n");
      out.write("    This is my JSP page. <br>\r\n");
      out.write("  </body>\r\n");
      out.write("</html>\r\n");
    } catch (Throwable t) {
      if (!(t instanceof SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try { out.clearBuffer(); } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}
我們看到,這個類繼承了org.apache.jasper.runtime.HttpJspBase,要想看到這個類的原始碼,我們需要下載tomcat的原始碼,然後找到這個類,原始碼如下:
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jasper.runtime;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.HttpJspPage;
import javax.servlet.jsp.JspFactory;

import org.apache.jasper.compiler.Localizer;

/**
 * This is the super class of all JSP-generated servlets.
 *
 * @author Anil K. Vijendran
 */
public abstract class HttpJspBase 
    extends HttpServlet 
    implements HttpJspPage 
        
    
{
    
    protected HttpJspBase() {
    }

    public final void init(ServletConfig config) 
	throws ServletException 
    {
        super.init(config);
	jspInit();
        _jspInit();
    }
    
    public String getServletInfo() {
	return Localizer.getMessage("jsp.engine.info");
    }

    public final void destroy() {
	jspDestroy();
	_jspDestroy();
    }

    /**
     * Entry point into service.
     */
    public final void service(HttpServletRequest request, HttpServletResponse response) 
	throws ServletException, IOException 
    {
        _jspService(request, response);
    }
    
    public void jspInit() {
    }

    public void _jspInit() {
    }

    public void jspDestroy() {
    }

    protected void _jspDestroy() {
    }

    public abstract void _jspService(HttpServletRequest request, 
				     HttpServletResponse response) 
	throws ServletException, IOException;
}
好吧,看到了,繼承了HttpServlet類,所以說其實Jsp就是一個Servlet

Jsp的語法:

1.JSP模版元素 
2.JSP表示式 
3.JSP指令碼片段
4.JSP註釋
5.JSP指令
6.JSP標籤 
7.JSP內建物件
8.如何查詢JSP頁面中的錯誤 

Jsp模板元素

JSP頁面中的HTML內容稱之為JSP模版元素。 
JSP模版元素定義了網頁的基本骨架,即定義了頁面的結構和外觀。

Jsp中的指令碼表示式

JSP指令碼表示式(expression)用於將程式資料輸出到客戶端
語法:<%= 變數或表示式 %>
舉例:當前時間:<%= new java.util.Date() %> 
JSP引擎在翻譯指令碼表示式時,會將程式資料轉成字串,然後在相應位置用out.print(…) 將資料輸給客戶端。
JSP指令碼表示式中的變數或表示式後面不能有分號(;)

如下圖:Jsp翻譯之後的Servlet中的指令碼表示式翻譯的結果:


就是將指令碼表示式中的程式碼原封不動的使用out.print()輸出

Jsp中的指令碼片段

JSP指令碼片斷(scriptlet)用於在JSP頁面中編寫多行Java程式碼。語法:
<% 
多行java程式碼 
%> 

注意:JSP指令碼片斷中只能出現java程式碼,不能出現其它模板元素, JSP引擎在翻譯JSP頁面中,會將JSP指令碼片斷中的Java程式碼將被原封不動地放到Servlet的_jspService方法中。 
JSP指令碼片斷中的Java程式碼必須嚴格遵循Java語法,例如,每執行語句後面必須用分號(;)結束。

在一個JSP頁面中可以有多個指令碼片斷,在兩個或多個指令碼片斷之間可以嵌入文字、HTML標記和其他JSP元素。
舉例:

<%
	int x = 10;
	out.println(x);
%>
<p>這是JSP頁面文字</p>
<%
	int y = 20;
	out.println(y);
%>

多個指令碼片斷中的程式碼可以相互訪問,猶如將所有的程式碼放在一對<%%>之中的情況。如:out.println(x);
單個指令碼片斷中的Java語句可以是不完整的,但是,多個指令碼片斷組合後的結果必須是完整的Java語句,例如:

<%
	for (int i=1; i<5; i++) 
	{
%>
	<H1>www.it315.org</H1>
<%
	}
%> 

Jsp的宣告

JSP頁面中編寫的所有程式碼,預設會翻譯到servlet的service方法中, 而Jsp宣告中的java程式碼被翻譯到_jspService方法的外面。語法:
<%! 
java程式碼
%>

所以,JSP宣告可用於定義JSP頁面轉換成的Servlet程式的靜態程式碼塊、成員變數和方法 。 
多個靜態程式碼塊、變數和函式可以定義在一個JSP宣告中,也可以分別單獨定義在多個JSP宣告中。
JSP隱式物件的作用範圍僅限於Servlet的_jspService方法,所以在JSP宣告中不能使用這些隱式物件。

<%!
static 
{ 
	System.out.println("loading Servlet!"); 
}
private int globalVar = 0;
public void jspInit()
{
	System.out.println("initializing jsp!");
}
%>
<%!
public void jspDestroy()
{
	System.out.println("destroying jsp!");
}
%>

Jsp註釋

JSP註釋的格式:
<%-- 註釋資訊 --%>
JSP引擎在將JSP頁面翻譯成Servlet程式時,忽略JSP頁面中被註釋的內容。 

Jsp指令

JSP指令(directive)是為JSP引擎而設計的,它們並不直接產生任何可見輸出,而只是告訴引擎如何處理JSP頁面中的其餘部分。在JSP2.0規範中共定義了三個指令: page指令 Include指令 taglib指令 JSP指令的基本語法格式:
<%@ 指令 屬性名="值" %>

首先我們來看一下page指令的用法

page指令用於定義JSP頁面的各種屬性,無論page指令出現在JSP頁面中的什麼地方,它作用的都是整個JSP頁面,為了保持程式的可讀性和遵循良好的程式設計習慣,page指令最好是放在整個JSP頁面的起始位置。 
JSP 2.0規範中定義的page指令的完整語法:
<%@ page 
[ language="java" ] 
[ extends="package.class" ] 
[ import="{package.class | package.*}, ..." ] 
[ session="true | false" ] 
[ buffer="none | 8kb | sizekb" ] 
[ autoFlush="true | false" ] 
[ isThreadSafe="true | false" ] 
[ info="text" ] 
[ errorPage="relative_url" ] 
[ isErrorPage="true | false" ] 
[ contentType="mimeType [ ;charset=characterSet ]" | "text/html ; charset=ISO-8859-1" ] 
[ pageEncoding="characterSet | ISO-8859-1" ] 
[ isELIgnored="true | false" ] 
%>

1.看一下通過page指令設定頁面的編碼:
舉例:<%@ page contentType="text/html;charset=gb2312"%>
這個指令的作用就相當於response.setContentType("text/html;charset=gb2312"); 但是這個指令和 <%@ page pageEncoding="gb2312"%>
的區別是: pageEncoding是jsp檔案本身的編碼
contentType的charset是指伺服器傳送給客戶端時的內容編碼
JSP要經過兩次的“編碼”,第一階段會用pageEncoding,第二階段會用utf-8至utf-8,第三階段就是由Tomcat出來的網頁, 用的是contentType。
第一階段是jsp編譯成.java,它會根據pageEncoding的設定讀取jsp,結果是由指定的編碼方案翻譯成統一的UTF-8 JAVA原始碼(即.java),如果pageEncoding設定錯了,或沒有設定,出來的就是中文亂碼。
第二階段是由JAVAC的JAVA原始碼至java byteCode的編譯,不論JSP編寫時候用的是什麼編碼方案,經過這個階段的結果全部是UTF-8的encoding的java原始碼。
JAVAC用UTF-8的encoding讀取java原始碼,編譯成UTF-8 encoding的二進位制碼(即.class),這是JVM對常數字串在二進位制碼(java encoding)內表達的規範。
第三階段是Tomcat(或其的application container)載入和執行階段二的來的JAVA二進位制碼,輸出的結果,也就是在客戶端見到的,這時隱藏在階段一和階段二的引數contentType就發揮了功效
contentType的設定.
pageEncoding 和contentType的預設都是 ISO8859-1. 而隨便設定了其中一個, 另一個就跟著一樣了(TOMCAT4.1.27是如此). 但這不是絕對的, 這要看各自JSPC的處理方式. 而pageEncoding不等於contentType, 更有利亞洲區的文字 CJKV系JSP網頁的開發和展示, (例pageEncoding=GB2312 不等於 contentType=utf-8)。
jsp檔案不像.java,.java在被編譯器讀入的時候預設採用的是作業系統所設定的locale所對應的編碼,比如中國大陸就是GBK,臺灣就是BIG5或者MS950。而一般我們不管是在記事本還是在ue中寫程式碼,如果沒有經過特別轉碼的話,寫出來的都是本地編碼格式的內容。所以編譯器採用的方法剛好可以讓虛擬機器得到正確的資料。
但是jsp檔案不是這樣,它沒有這個預設轉碼過程,但是指定了pageEncoding就可以實現正確轉碼了。
因為我們使用了MyEclipse開發工具,只要我們設定其中一個編碼就會將該檔案的編碼以及頁面訪問編碼都設定好了,例如: 我們使用指令: <%@ page contentType="text/html;charset=gb2312"%>
我們看一下翻譯之後的servlet程式碼:
我們在來看一下這個檔案的編碼:
可以看到都是gb2312的編碼, 同樣我們在使用指令: <%@ page pageEncoding="utf-8"%>
效果和上面一樣的,會同時將檔案的編碼和頁面瀏覽的編碼都設定成了utf-8編碼,這些動作都是得益於MyEclipse的智慧,如果你使用txt文字開發的話,就不會有這樣的結果了。 那麼當我們同時使用這兩個命令的話是什麼樣的結果呢: <%@ page contentType="text/html;charset=gb2312"%>
<%@ page pageEncoding="utf-8"%>
這時候我就會發現,檔案的編碼是utf-8,而頁面瀏覽的編碼是gb2312,所以說上面的一條指令是專門設定頁面瀏覽的編碼的,下面的指令是專門設定檔案的儲存編碼的。出現以上的結果都是因為使用了MyEclipse開發工具。 2.通過page指令匯入java包: JSP 引擎自動匯入下面的包:
java.lang.*
javax.servlet.*
javax.servlet.jsp.*
javax.servlet.http.*
可以在一條page指令的import屬性中引入多個類或包,其中的每個包或類之間使用逗號分隔:
<%@ page import="java.util.Date,java.sql.*,java.io.*"%>
上面的語句也可以改寫為使用多條page指令的import屬性來分別引入各個包或類:
<%@ page import="java.util.Date"%>
<%@ page import="java.sql.*"%>
<%@ page import="java.io.*"%>
3.下面在來看一下buffer屬性: <%@ page buffer="4kb" %>
使用這個屬性是指定out物件的快取大小,關於out物件,我們後面會說到,比如我們這裡設定了out的緩衝區是4kb,我們可以看一下翻譯後的程式碼:
如果我們想關閉緩衝區的話,只需要設定值為none就可以了 4.下面在來看一下isThreadSafe屬性: 這個屬性見名知意,是設定是否執行緒安全的,我們在之前討論Servlet的時候,說到了Servlet是個單例物件,是執行緒不安全的,我們那時候可以通過實現一個介面來實現執行緒安全,這裡只需要設定這個屬性值就可以控制Jsp翻譯之後的Servlet時候執行緒安全: <%@ page isThreadSafe="true|false" %>  預設值為true
isThreadSafe=false模式表示它是以Singleton模式執行。
     該模式implements了介面SingleThreadMode,
     該模式同一時刻只有一個例項,不會出現資訊同步與否的概念。
     若多個使用者同時訪問一個這種模式的頁面,
     那麼先訪問者完全執行完該頁面後,後訪問者才開始執行。
isThreadSafe=true模式表示它以多執行緒方式執行。
    該模式的資訊同步,需訪問同步方法(用synchronized標記的)來實現。
我們將值設定成false之後發現翻譯Jsp之後的Servlet:
實現了SingleThreadModel介面。 5.下面在來看一下session屬性: <%@ page session="false|true"%> 預設值是true 是指不能在本頁使用session.也就是在本頁面禁用了session
就是在將Jsp翻譯成Servlet的時候不會傳遞Session物件了,比如我們將他設定成false,檢視翻譯後的Servlet原始碼:

我們發現並沒有HttpSession物件,所以我們也不能在Jsp頁面中使用session物件了 6.下面來看一下errorPage屬性 這個屬性是設定伺服器端出錯之後的錯誤頁面的,比如我們在編寫伺服器程式碼的時候,突然丟擲異常,那麼會返回一個500,這樣給使用者的感覺就很噁心,所以我們要做的人性化一點,就是通過這個屬性值,設定一個錯誤頁面,當伺服器發生錯誤的時候都會跳轉到這個頁面中,比如: <%@ page errorPage="/error.jsp"%> 然後我們可以在Jsp中新增一個指令碼片段:
<%
	int x = 1/0;
%>

這樣就會丟擲異常,我們訪問這個Jsp看一下效果:
轉到了錯誤頁面,同時我們觀察位址列可以發現這裡面使用的是轉發技術,所以我們在書寫url地址的時候,那個開始的斜槓代表是當前應用,因為這個地址是給伺服器用的(這個原則我們在之前說過了) 這裡在拓展一下,如果我們想給應用配置一個全域性的錯誤頁面我們該怎麼配置呢? 這個想一想肯定是在web.xml檔案中做配置:
<!-- 錯誤頁面問題同時jsp中的errorPage命令的優先順序比較高 -->
	<error-page>
		<exception-type>Exception</exception-type>
		<location>/error.jsp</location>
	</error-page>
	
	<error-page>
		<error-code>404</error-code>
		<location>/error.jsp</location>
	</error-page>
	
	<error-page>
		<error-code>500</error-code>
		<location>/error.jsp</location>
	</error-page>
我們看到這裡可以配置丟擲的異常型別所對應的錯誤頁面,也可以配置狀態碼對應的錯誤頁面,而且這裡配置的結果的優先順序沒有errorPage屬性配置錯誤頁面的優先順序高。 7.同時還有一個屬性就是isErrorPages: <@ page isErrorPages="false|true"%> 預設值是true 是否開啟錯誤頁面,就是控制上面的errorPage屬性的作用開關的 8.屬性isELIgnored <@ page isELIgnored="false|true"%> 預設值是false 是否在該頁面中忽視EL表示式的功能,這個我們一般都不去做設定,因為我們在頁面中肯定會使用到EL表示式的 注意:如果一個指令有多個屬性,這多個屬性可以寫在一個指令中,也可以分開寫。
例如:

<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.Date"%>
也可以寫作:
<%@ page contentType="text/html;charset=gb2312" import="java.util.Date"%> 

下面在來看一下include指令

include指令很簡單,就是實現頁面包含的,我們之前在介紹request的時候,說到使用程式碼進行頁面包含,那時候我們說到了,這個程式碼來實現頁面包含是動態包含,而使用include指令來實現頁面包含是靜態包含,關於靜態包含和動態包含,我們在下面介紹jsp:include標籤的時候在詳細說明

最後來看一下taglib指令

這個指令作用也是很簡單的,就是引入標籤,這個之後再我們後面接收JSTL的內容的時候在作介紹

Jsp中內建的9個隱式物件

每個JSP 頁面在第一次被訪問時,WEB容器都會把請求交給JSP引擎(即一個Java程式)去處理。JSP引擎先將JSP翻譯成一個_jspServlet(實質上也是一個servlet) ,然後按照servlet的呼叫方式進行呼叫。
由於JSP第一次訪問時會翻譯成servlet,所以第一次訪問通常會比較慢,但第二次訪問,JSP引擎如果發現JSP沒有變化,就不再翻譯,而是直接呼叫,所以程式的執行效率不會受到影響。
JSP引擎在呼叫JSP對應的_jspServlet時,會傳遞或建立9個與web開發相關的物件供_jspServlet使用。JSP技術的設計者為便於開發人員在編寫JSP頁面時獲得這些web物件的引用,特意定義了9個相應的變數,開發人員在JSP頁面中通過這些變數就可以快速獲得這9大物件的引用。
這9個物件分別是哪些,以及作用也是筆試經常考察的知識點。
request
response
config
application
exception
Session
page
out
pageContext


request:HttpServletRequest
response:HttpServletResponse
session: HttpSession
application: servletContext
config:servletConfig
out:JspWriter
exception
page:this
pageContext

上面其實很多物件我們都接觸過了,之前介紹servlet的時候都介紹過了,關於那個exception是個異常物件,只有當我們的Jsp頁面中丟擲異常的時候,才會有這個物件的產生,否則是不會傳遞這個物件的,至於page物件,這個很簡單就是當前物件,即jsp翻譯後的servlet物件,那麼下面就來詳細解釋一下out物件和pageContext物件了。 out隱式物件用於向客戶端傳送文字資料。 
out物件是通過呼叫pageContext物件的getOut方法返回的,其作用和用法與ServletResponse.getWriter方法返回的PrintWriter物件非常相似。 
JSP頁面中的out隱式物件的型別為JspWriter,JspWriter相當於一種帶快取功能的PrintWriter,設定JSP頁面的page指令的buffer屬性可以調整它的快取大小,甚至可以關閉它的快取。 
只有向out物件中寫入了內容,且滿足如下任何一個條件時,out物件才去呼叫ServletResponse.getWriter方法,並通過該方法返回的PrintWriter物件將out物件的緩衝區中的內容真正寫入到Servlet引擎提供的緩衝區中:

1.設定page指令的buffer屬性關閉了out物件的快取功能
2.out物件的緩衝區已滿
3.整個JSP頁面結束

下面來看一下例項:
<%
    	out.print("aaa");
    	response.getWriter().write("bbb");
%>
我們訪問一下MyJsp.jsp頁面:
我們看到了,我們是先向瀏覽器中輸出aaa,然後在輸出bbb,但是結果是相反的,原因就是out物件是JspWriter,是帶有緩衝的。看下圖解釋:
所以我們以後再給瀏覽器輸出資料的時候一定要注意,最好不要同時使用這兩個物件,一般我們只是用out物件進行輸出資料的。 pageContext物件是JSP技術中最重要的一個物件,它代表JSP頁面的執行環境,這個物件不僅封裝了對其它8大隱式物件的引用,它自身還是一個域物件(之前我們介紹了三個域物件:ServletContext,Session,Request),這個域物件的生命週期最短,作用域最小,他的作用域就是當前的jsp頁面,當然它可以用來儲存資料。並且,這個物件還封裝了web開發中經常涉及到的一些常用操作,例如引入和跳轉其它資源、檢索其它域物件中的屬性等。 
getException方法返回exception隱式物件 
getPage方法返回page隱式物件
getRequest方法返回request隱式物件 
getResponse方法返回response隱式物件 
getServletConfig方法返回config隱式物件
getServletContext方法返回application隱式物件
getSession方法返回session隱式物件 
getOut方法返回out隱式物件

pageContext封裝其它8大內建物件的意義,思考:如果在程式設計過程中,把pageContext物件傳遞給一個普通java物件,那麼這個java物件將具有什麼功能?  
這個我們在後面會介紹自定義標籤的時候,這個用途就體現出來了,我們只需要傳遞一個pageContext物件,就可以操作其他多個物件了,很方便的 pageContext物件中的操作域中資料的方法 
public void setAttribute(java.lang.String name,java.lang.Object value)
public java.lang.Object getAttribute(java.lang.String name)
public void removeAttribute(java.lang.String name)

pageContext物件中還封裝了訪問其它域的方法(和上面的方法不同之處就是多了一個引數,這個引數是可以直接指定相應的域)
public java.lang.Object getAttribute(java.lang.String name,int scope)
public void setAttribute(java.lang.String name, java.lang.Object value,int scope)
public void removeAttribute(java.lang.String name,int scope)

代表各個域的常量
PageContext.APPLICATION_SCOPE
PageContext.SESSION_SCOPE
PageContext.REQUEST_SCOPE
PageContext.PAGE_SCOPE 
下面這個方法是非常重要的,因為這個方法是在所有的域中查詢資料,查詢順序是:pageContext->request->session->ServletContext,如果查詢不到相應的資料的話,就返回一個空字串,這個方法和之後要說到的el表示式的功能是一樣的
findAttribute方法  (*重點,查詢各個域中的屬性)
到此為止,web開發接觸到了4個域物件:
pageContext(稱之為page域) 
request(稱之為request域)
session(稱之為session域)
servletContext(稱之為application域)
這4個域物件是學習web的重點,也是筆試經常考察的知識點。
明確如下問題:
這4個物件的生命週期?
什麼是域?為什麼把這4個物件叫做域物件呢?
哪種情況下用哪種域物件。
PageContext類中定義了一個forward方法和兩個include方法來分別簡化和替代RequestDispatcher.forward方法和include方法

傳遞給這些方法的資源路徑都只能是相對路徑,如果路徑以“/”開頭,表示相對於當前WEB應用程式的根目錄,否則,表示相對於當前JSP所對映到的訪問路徑。

JSP標籤庫

雖然我們希望JSP頁面僅用作資料顯示模組,不要巢狀任何java程式碼引入任何業務邏輯,但在實際開發中不引入一點業務邏輯是不可能的,但引入業務邏輯會導致頁面出現難看java程式碼,怎麼辦?
Sun公司允許使用者開發自定義標籤封裝頁面的java程式碼,以便jsp頁面不出現一行java程式碼。當然sun公司在jsp頁面中也內建了一些標籤(這些標籤叫做jsp標籤),開發人員使用這些標籤可以完成頁面的一些常用業務邏輯。
JSP標籤也稱之為Jsp Action(JSP動作)元素,它用於在JSP頁面中提供業務邏輯功能。
<jsp:include>標籤  
<jsp:forward>標籤  
<jsp:param>標籤  
<jsp:useBean>標籤 <jsp:setProperty>標籤 <jsp:getProperty>標籤

1.<jsp:include>標籤

<jsp:include>標籤用於把另外一個資源的輸出內容插入進當前JSP頁面的輸出內容之中,這種在JSP頁面執行時的引入方式稱之為動態引入。
語法:
<jsp:include page="relativeURL | <%=expression%>" flush="true|false" />
 
page屬性用於指定被引入資源的相對路徑,它也可以通過執行一個表示式來獲得。
flush屬性指定在插入其他資源的輸出內容時,是否先將當前JSP頁面的已輸出的內容重新整理到客戶端。  
<jsp:include>標籤是動態引入(和使用程式碼進行include一樣),<jsp:include>標籤涉及到的2個JSP頁面會被翻譯成2個servlet,這2個servlet的內容在執行時進行合併。 而include指令是靜態引入(編譯時引入),涉及到的2個JSP頁面會被翻譯成一個servlet,其內容是在原始檔級別進行合併。不管是<jsp:include>標籤,還是include指令,它們都會把兩個JSP頁面內容合併輸出,所以這兩個頁面不要出現重複的HTML全域性架構標籤,否則輸出給客戶端的內容將會是一個格式混亂的HTML文件。
例子: 使用<jsp:include>標籤來實現包含頁面:
<jsp:include page="/head.jsp"></jsp:include>
<jsp:include page="/foot.jsp"></jsp:include>
我們到tomcat的work目錄中看一下:

我們看到,會將head.jsp和foot.jsp單獨翻譯成servlet,這個就是動態包含 下面在看一下使用include指令實現頁面包含:
<%@ include file="/head.jsp" %>
這時候我們發現work目錄中並不會還單獨翻譯head.jsp頁面了,同時我們看看MyJsp頁面翻譯的servlet程式碼:
我們看到在程式碼中使用靜態程式碼塊實現靜態頁面包含的。 <jsp:include>標籤:使用page屬性指定被引入資源。
include指令:使用file屬性指定被引入資源。
假設myweb應用的根目錄下有一個a.jsp檔案如果將a.jsp頁面對映成了如下地址:
http://localhost:8080/myweb/dir1/a.html
在a.jsp頁面中使用瞭如下語句引入b.jsp檔案:
<jsp:include page="b.jsp" />
請問:b.jsp要位於什麼位置,上面的include才不會出錯? 
http://localhost:8080/myweb/b.jspf
http://localhost:8080/myweb/dir1/b.jspf
假設myweb應用程式的根目錄下有一個a.jsp檔案,如果將a.jsp頁面對映為如下地址:
http://localhost:8080/myweb/dir1/a.html
在a.jsp頁面中使用瞭如下語句引入b.jspf檔案:
<%@ include file=“b.jspf”%>
請問: b.jspf要位於什麼位置,上面的include才不會出錯?
http://localhost:8080/myweb/b.jspf
http://localhost:8080/myweb/dir1/b.jspf

2.<jsp:forward>標籤

<jsp:forward>標籤用於把請求轉發給另外一個資源。
語法:
<jsp:forward page="relativeURL | <%=expression%>" /> 

page屬性用於指定請求轉發到的資源的相對路徑,它也可以通過執行一個表示式來獲得。

3.<jsp:param>或者<jsp:params>標籤

當使用<jsp:include>和<jsp:forward>標籤引入或將請求轉發給其它資源時,可以使用<jsp:param>標籤向這個資源傳遞引數。
語法1:
<jsp:include page="relativeURL | <%=expression%>">
<jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
</jsp:include>
語法2:
<jsp:forward page="relativeURL | <%=expression%>">
<jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
</jsp:include>

<jsp:param>標籤的name屬性用於指定引數名,value屬性用於指定引數值。在<jsp:include>和<jsp:forward>標籤中可以使用多個<jsp:param>標籤來傳遞多個引數。 

4.<jsp:useBean>標籤,<jsp:setProperty>標籤,<jsp:getProperty>標籤

這三個標籤是一起用來操作bean物件的,<jsp:useBean>是用來初始化bean物件的,<jsp:setProperty>標籤是用來設定bean物件中的屬性值,<jsp:getProperty>標籤是用來獲取bean物件中的屬性值的,例子:
<body>
    <!-- 找到就直接用,找不到例項化 scope預設是page域-->
  	<jsp:useBean id="person" class="com.weijia.domain.Person" scope="page"/>
  	
  	<!-- 直接設定屬性值(8種基本型別的轉換) -->
  	<jsp:setProperty name="person" property="name" value="xxx"/>
  	
  	<!-- 用請求引數給屬性賦值(8中基本型別的轉換) -->
  	<jsp:setProperty name="person" property="name" param="name"/>
  	
  	<!-- 獲取Person中的屬性name的值 -->
  	<jsp:getProperty name="person" property="name"/>
  </body>
同時我們定義了一個Bean物件:com.weijia.domain.Person,其中有一個name屬性(一定要有get/set方法才叫屬性) 對於標籤<jsp:useBean>他可以指定在哪個域中建立這個bean物件,他的規則是首先在這個域中查詢有沒有這個物件,沒有就建立,有就直接拿來使用。 對於<jsp:setProperty>標籤,可以直接使用value屬性設定屬性的值,這裡面他內部是有一個型別轉換的,可以將字串值轉化成8中基本型別的值,其他物件型別的轉化是會報錯的,比如我們現在Person類中有一個屬性birthday是Date型的,那麼我們這裡就不能直接寫成這樣:
<jsp:setProperty name="person" property="birthday" value="1990-08-01"/>
這樣系統會報型別轉化錯誤, 當然我們可以直接使用指令碼表示式給這個屬性值傳遞一個Date型別的物件,這樣就不會又錯了:
<jsp:setProperty name="person" property="birthday" value="<%=new Date()%>"/>
當然我們也可以使用請求引數來設定屬性值:
<jsp:setProperty name="person" property="name" param="name"/>
我們訪問:http://localhost:8080/JspDemo/bean.jsp?name=jiangwei,這樣就可以進行屬性賦值 同時這裡的name屬性中的填的是<jsp:useBean>標籤中的id值,所以說,當我們在使用get/setProperty標籤的時候一定是在useBean標籤之後 關於取出屬性值的標籤<jsp:getProperty>的話就簡單了。 下面來看一下怎麼配置jsp的訪問路徑
<servlet>
	<servlet-name>SimpleJspServlet</servlet-name>
	<jsp-file>/jsp/simple.jsp</jsp-file>
	<load-on-startup>1</load-on-startup >
</servlet>
	……
<servlet-mapping>
	<servlet-name>SimpleJspServlet</servlet-name>
	<url-pattern>/xxx/yyy.html</url-pattern>
</servlet-mapping>

Jsp中怎麼排查錯誤

JSP頁面中的JSP語法格式有問題,導致其不能被翻譯成Servlet原始檔,JSP引擎將提示這類錯誤發生在JSP頁面中的位置(行和列)以及相關資訊。
JSP頁面中的JSP語法格式沒有問題,但被翻譯成的Servlet原始檔中出現了Java語法問題,導致JSP頁面翻譯成的Servlet原始檔不能通過編譯,JSP引擎也將提示這類錯誤發生在JSP頁面中的位置(行和列)以及相關資訊。
JSP頁面翻譯成的Servlet程式在執行時出現異常,這與普通Java程式的執行時錯誤完全一樣,Java虛擬機器將提示錯誤發生在Servlet原始檔中的位(行和列)以及相關資訊。