JSP介紹
JSP全稱java server pages和servlets一樣都是sun公司定義的一種用於開發動態web資源的技術。
JSP這門技術的最大的特點在於,寫jsp就像在寫html,但它相比html而言,html只能為用戶提供靜態數據,而Jsp技術允許在頁面中嵌套java代碼,為用戶提供動態數據。
JSP運行原理
瀏覽器向服務器發請求,不管訪問的是什么資源,其實都是在訪問Servlet,所以當訪問一個jsp頁面時,其實也是在訪問一個Servlet,服務器在執行jsp的時候,首先把jsp翻譯成一個Servlet,所以我們訪問jsp時,其實不是在訪問jsp,而是在訪問jsp翻譯過后的那個Servlet。
當我們通過瀏覽器訪問index.jsp時,服務器首先將index.jsp翻譯成一個index_jsp.class,在Tomcat服務器的work\Catalina\localhost\項目名\org\apache\jsp目錄下可以看到index_jsp.class的源代碼文件index_jsp.java。代碼如下:
從上面的代碼可以發現b_jsp是繼承org.apache.jasper.runtime.HttpJspBase的,但是這個類其實是繼承HttpServlet的,所以其本質上訪問b.jsp還是在訪問servlet。
JSP的排版標簽是如何發送到瀏覽器的
在jsp中編寫的java代碼和html代碼都會被翻譯到_jspService方法中去,在jsp中編寫的java代碼會原封不動地翻譯成java代碼,如<%out.print("Hello Jsp");%>直接翻譯成out.print("Hello Jsp”);,而HTML代碼則會翻譯成使用out.write("<html標簽>\r\n");的形式輸出到瀏覽器。在jsp頁面中編寫的html排版標簽都是以out.write("<html標簽>\r\n");的形式輸出到瀏覽器,瀏覽器拿到html代碼后才能夠解析執行html代碼。
JSP頁面中的java代碼服務器是如何執行的
在jsp中編寫的java代碼會被翻譯到_jspService方法中去,當執行_jspService方法處理請求時,就會執行在jsp編寫的java代碼了,所以Jsp頁面中的java代碼服務器是通過調用_jspService方法處理請求時執行的。
web服務器在調用jsp的時候會提供jsp8個對象使用
PageContext pageContext,HttpSession session,ServletContext application,ServletConfig config,JspWriter out,Object page=null,HttpServletRequest request,HttpServletResponse response。這8個對象是可以直接在jsp頁面中使用的。
其中的request和response和page是已經完成了初始化,而其它的5個通過下面的代碼完成初始化。
jsp的整個運行流程。
1.瀏覽器對服務器發送請求,請求是動態的。連接器將請求交給Servlet容器處理,
2.servlet容器查找要執行的.jsp文件
3.將jsp轉為.java,
4.在編譯為.class,
5.最終服務器要加載這個class文件,最后調用這個servlet實例。
注意:第二次執行的時候,如果內容沒有發生變化,則直接執行原來的class文件。
JSP模版元素
jsp頁面中的HTML內容稱之為jsp模版元素。
jsp模版元素定義了網頁的基本骨架,即定義了頁面的結構和外觀。
JSP表達式
JSP腳本表達式(expression)用於將程序數據輸出到客戶端。
語法:<%= 變量或表達式 %>
例如:<%= 5 %> 將會將5輸出到客戶端。
JSP腳本片段
JSP腳本片斷(scriptlet)用於在JSP頁面中編寫多行Java代碼。
語法:<%
多行java代碼
%>
在<% %>中可以定義變量、編寫語句,不能定義方法。
注意:
1.jsp腳本片段中只能出現java代碼不可以出現其他jsp模版元素。
2.jsp腳本片段中的所有代碼必須遵循java語法規范。
3.一個jsp中可以有多個jsp腳本片段,之間可以加其它的元素。
4.多個腳本片段中的代碼可以互相訪問。
5.單個腳本片段中的代碼可以是不完整的,但是多個腳本片段組合后必須是完整的。
JSP聲明
JSP頁面中編寫的所有代碼,默認會翻譯到servlet的_jspservice方法中, 而Jsp聲明中的java代碼被翻譯到_jspService方法的外面。
語法:<%!
java代碼
%>
所以,JSP聲明可用於定義JSP頁面轉換成的Servlet程序的靜態代碼塊、成員變量和方法 。
JSP指令
JSP指令(directive)是為JSP引擎而設計的,它們並不直接產生任何可見輸出,而只是告訴引擎如何處理JSP頁面中的其余部分。JSP中定義了三個指令,分別是page、include、taglib。
JSP指令的基本語法格式:<%@ 指令 屬性名="值" %>。
page指令
page指令用於定義JSP頁面的各種屬性,無論page指令出現在JSP頁面中的什么地方,它作用的都是整個JSP頁面,為了保持程序的可讀性和遵循良好的編程習慣,page指令最好是放在整個JSP頁面的起始位置。
—Language屬性:指定在頁面中的腳本語言。
—contentType:表示JSP輸出的MIME類型和編碼。
—pageEncoding:表示jsp的輸出編碼方式。
page指令的import屬性
—可以在一條page指令的import屬性中引入多個類或包,其中的每個包或類之間使用逗號,分隔.
<% @ page import="java.util.*,java.net" %>
page指令的errorPage屬性
該屬性用於頁面出錯的時候,指明跳轉到哪一個頁面。
—errorPage屬性的設置值必須使用相對路徑,如果以“/”開頭,表示相對於當前Web應用程序的根目錄(注意不是站點根目錄),否則,表示相對於當前頁面。
—可以在web.xml文件中使用<error-page>元素為整個Web應用程序設置錯誤處理頁面。
—<error-page>元素有3個子元素,<error-code>、<exception-type>、<location>
—<error-code>子元素指定錯誤的狀態碼,例如:<error-code>404</error-code>
—<exception-type>子元素指定異常的類的完全限定名
—<location>子元素指定以“/”開頭的錯誤處理頁面的路徑。即/ErrorPage/404Error.jsp類似。
—如果設置了某個JSP頁面的errorPage屬性,那么在web.xml文件中設置的錯誤處理將不對該頁面起作用。
使用page指令的的isErrorPage屬性顯式聲明頁面為錯誤頁面
如果一個頁面為系統的錯誤頁面,應該用屬性isErrotPage為true顯示的聲明。使用該屬性后,jsp引擎在將error.jsp轉為servlet的時候,會在jspservice方法中生成一個exception對象,通過調用exception.getErrorMessage()可以獲取到錯誤的具體信息。
include的指令
@include可以包含任意的文件,當然,只是把文件的內容包含進來。
語法:<%@ include file=“relativeURL”%> —靜態包含。
include指令用於引入其它JSP頁面,如果使用include指令引入了其它JSP頁面,那么JSP引擎將把這兩個JSP在翻譯階段合並成一個java文件。所以include指令引入通常也稱之為靜態引入。
一、JSP運行原理
每個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大對象的引用。
out對象
該對象是通過pageContext的getout方法獲得,其類似於PrintWriter,但是它在PrintWriter基礎上增加了自己的一個out緩存區,意味着調用out.write並不會直接將數據寫入response的緩存區,而是將數據寫入out的緩存區,當出現下面3種情況時out對象才去調用ServletResponse.getWriter方法,並通過該方法返回的PrintWriter對象將out對象的緩沖區中的內容真正寫入到Servlet引擎提供的緩沖區中。
—page指令將buffer區關閉了。
—JSP頁面加載結束了。
—out對象的緩沖區滿了。
pageContext對象
pageContext對象是JSP技術中最重要的一個對象,它代表JSP頁面的運行環境,這個對象不僅封裝了對其它8大隱式對象的引用,它自身還是一個域對象(容器),可以用來保存數據。並且,這個對象還封裝了web開發中經常涉及到的一些常用操作,例如引入和跳轉其它資源、檢索其它域對象中的屬性等。
通過pageContext獲得其他對象
—getException方法返回exception隱式對象
—getPage方法返回page隱式對象
—getRequest方法返回request隱式對象
—getResponse方法返回response隱式對象
—getServletConfig方法返回config隱式對象
—getServletContext方法返回application隱式對象
—getSession方法返回session隱式對象
—getOut方法返回out隱式對象
pageContext封裝其它8大內置對象的意義
如果在編程過程中,把pageContext對象傳遞給一個普通java對象,那么這個java對象將可以獲取8大隱式對象,此時這個java對象就可以和瀏覽器交互了,此時這個java對象就成為了一個動態web資源了,這就是pageContext封裝其它8大內置對象的意義,把pageContext傳遞給誰,誰就能成為一個動態web資源,那么什么情況下需要把pageContext傳遞給另外一個java類呢,什么情況下需要使用這種技術呢,在比較正規的開發中,jsp頁面是不允許出現java代碼的,如果jsp頁面出現了java代碼,那么就應該想辦法把java代碼移除掉,我們可以開發一個自定義標簽來移除jsp頁面上的java代碼,首先圍繞自定義標簽寫一個java類,jsp引擎在執行自定義標簽的時候就會調用圍繞自定義標簽寫的那個java類,在調用java類的時候就會把pageContext對象傳遞給這個java類,由於pageContext對象封裝了對其它8大隱式對象的引用,因此在這個java類中就可以使用jsp頁面中的8大隱式對象(request,response,config,application,exception,Session,page,out)了,pageContext對象在jsp自定義標簽開發中特別重要。
pageContext作為域對象
pageContext對象可以作為容器來使用,因此可以將一些數據存儲在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)
—public java.lang.Object findAttribute(java.lang.String name)
當要查找某個屬性時,findAttribute方法按照查找順序"page→request→session→application"在這四個對象中去查找,只要找到了就返回屬性值,如果四個對象都沒有找到要查找的屬性,則返回一個null。
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引入和跳轉到其他資源
PageContext類中定義了一個forward方法(用來跳轉頁面)和兩個include方法(用來引入頁面)來分別簡化和替代RequestDispatcher.forward方法和include方法。
pagecontext.forward(uri) pagecontext.include(uri)一般不用這種方法。
JSP的屬性范圍
JSP中提供了四種屬性范圍,四種屬性范圍分別指以下四種:
—當前頁:一個屬性只能在一個頁面中取得,跳轉到其他頁面無法取得。
—一次服務請求:一個頁面中設置的屬性,只要經過了服務器跳轉,則跳轉之后的頁面可以繼續取得。
—一次會話:一個用戶設置的內容,只要是與此用戶相關的頁面都可以訪問(一個會話表示一個人,這個人設置的東西只要這個人不走,就依然有效)。
—上下文中:在整個服務器上設置的屬性,所有人都可以訪問。
該四種屬性范圍都具有setAttribute和getAttribute和removeAttribute方法。
page屬性范圍
當服務器發生了一次跳轉后,當前頁面的屬性在跳轉后的頁面是不能夠獲得的。
a.jsp
b.jsp
瀏覽器結果
request屬性范圍
request屬性范圍,在服務器發生跳轉的時候,該屬性會傳遞下去。簡單說就是你的瀏覽器的url沒有變,則這個屬性可以一直使用。
<% request.setAttribute("name","yu"); %>
<% pageContext.forward("b.jsp"); %>
<% String name = (String)request.getAttribute("name"); %>
<p>這是一個錯誤的界面</p>
<%=name %>
session屬性范圍
session屬性范圍不管是服務端跳轉還是客戶端跳轉都可以繼續操作該屬性。
application屬性范圍
application是在服務器上設置的一個屬性,所以一旦設置了,任何用戶都可以瀏覽到此屬性。
JSP標簽
JSP標簽也稱之為Jsp Action(JSP動作)元素,它用於在Jsp頁面中提供業務邏輯功能,避免在JSP頁面中直接編寫java代碼,造成jsp頁面難以維護。
<jsp:include>
<jsp:include>標簽用於把另外一個資源的輸出內容插入進當前JSP頁面的輸出內容之中,這種在JSP頁面執行時的引入方式稱之為動態引入。
語法:<jsp:include page="relativeURL | <%=expression%>" flush="true|false" />
page屬性用於指定被引入資源的相對路徑,它也可以通過執行一個表達式來獲得。
flush屬性指定在插入其他資源的輸出內容時,是否先將當前JSP頁面的已輸出的內容刷新到客戶端。
<jsp:include>標簽與include指令的區別
<jsp:include>標簽是動態引入, <jsp:include>標簽涉及到的2個JSP頁面會被翻譯成2個servlet,這2個servlet的內容在執行時進行合並。
而include指令是靜態引入,涉及到的2個JSP頁面會被翻譯成一個servlet,其內容是在源文件級別進行合並。
jspf擴展名文件在jsp:include和@include的區別。
注意:只有使用@include包含jspf文件的時候,文件內容才能被解析執行。而使用jsp:include動態包含的時候會在客戶端輸出文件的原內容。本身動態包含是將多個文件翻譯為多個servlet,然后將執行的結果進行合並輸出,但是jspf文件在使用動態包含的時候被當作純文本文件處理了,即該jspf文件並沒有被翻譯為servlet執行,僅僅是當作文本進行輸出了。
如何讓服務器解析jspf文件呢
方法一:修改web應用的web.xml文件,添加映射
方法二:修改服務器上的web.xml文件,添加類似上面的映射。
<jsp:forward>標簽
該標簽用於將請求轉發給另一個資源。
語法:<jsp:forward page="relativeURL | <%=expression%>" />。服務器端跳轉。
<jsp:param>標簽
當使用<jsp:include>和<jsp:forward>標簽引入或將請求轉發給其它資源時,可以使用<jsp:param>標簽向這個資源傳遞參數。
語法一:
<jsp:include page="relativeURL | <%=expression%>">
<jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
</jsp:include>
語法二:
<jsp:forward page="relativeURL | <%=expression%>">
<jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
</jsp:include>
<jsp:param>標簽的name屬性用於指定參數名,value屬性用於指定參數值。在<jsp:include>和<jsp:forward>標簽中可以使用多個<jsp:param>標簽來傳遞多個參數。