JSP(java server page)
JSP是一種語法規範,在html模板中嵌入jsp語法,其實現處理的方式是把jsp文件轉換爲servlet類。
當請求到達時,服務器若判別爲jsp請求就會轉交給jsp引擎處理,它會找到jsp文件並根據語法解析爲servlet類文件,然後編譯並加載該servlet(實際上jsp引擎會檢查相應servlet是否已加載且加載時間是否在jsp最後更改時間之後,只有該條件不成立纔會重新生成servlet並重新編譯加載,這樣減少生成編譯和加載servlet的過程以提高訪問效率)。可見jsp語法其實就是怎麼將jsp文件轉換爲servlet的語法(jsp轉換爲servlet只是實現jsp規範的一種可行方案)。
JSP的生命週期
JSP文件生成的servlet都實現了HttpJspPage接口(Servlet的子接口),對於tomcat而言,jsp頁面生成的servlet都繼承自HttpJspPage的實現類HttpJspBase。其生命週期爲:根據jsp文件生成並加載servlet->jspInit->_jspService->jspDestroy,當jsp文件發生更改重新生成並加載servlet時會先銷燬系統中原來的servlet,也就是會調用老的servlet的jspDestroy方法並調用新的servlet的jspInit方法。
JSP語法
空jsp文件生成的servlet主要只有一個_jspService方法,該方法分爲兩段,第一段把幾個有用的對象(隱式對象)列了出來,因此後面的第二段可以使用這些對象;第二段爲jsp文件的某些部分生成。解析jsp文件的時候就是把jsp的各個部分按順序插入servlet源文件的相應地方以及對插入的控制。隱式對象有9個,常用的有request(HttpServletRequst)、response(HttpServletResponse)、out(JspWriter類型表示響應的輸出流)、session(HttpSession)、application(ServletContext)、config(ServletConfig)、page(就是this關鍵字)、pageContext(PageContext類的實例,提供對JSP頁面所有對象以及命名空間的訪問)、exception(當某個頁面爲異常處理頁面時,該值爲異常發生頁面拋出的異常)。
- 模板部分(除去以下部分的其他部分),該部分的內容會封裝爲out.write(“模板部分”)插入_jspService的第二部分。
- 註釋部分(<%-- 註釋部分 --%>),註釋部分的內容直接被忽略,不會寫入servlet,但<!-- html註釋 -->會寫入servlet,只是在客戶端不會解析顯示而已。
- 表達式部分(<%= 表達式部分 %>),該部分的內容會封裝爲out.print(表達式部分)直接插入_jspService的第二部分。
- 腳本部分(<% 腳本部分 %>),該部分的內容會直接插入_jspService的第二部分。
- 聲明部分(<%! 聲明部分 %>),該部分的內容可以聲明或定義一個或多個變量、方法,會被直接寫入servlet類中與_jspService平行部分,也就是servlet的成員變量和成員函數。
- 指令部分(<%@ directive attribute=“value” %>),用來設置整個JSP頁面相關的屬性,如網頁的編碼方式和腳本語言。其中directive可以有三個取值page、include或taglib。
- 標籤部分 (<prefix:tag attribute=“attr”> 標籤內容 </prefix:tag>),在_jspService的第二部分新建一個Tag對象並依次調用其生命週期方法。
- 動作部分 (<jsp:action_name attribute=“value” />),JSP動作元素在請求處理階段起作用。
JSP指令
JSP指令用來設置整個JSP頁面相關的屬性,如網頁的編碼方式和腳本語言,格式爲<%@ directive attribute=“value” %>,其中directive可以有三個取值page、include或taglib。
當指令部分的directive爲taglib時,表示引入標籤庫,格式爲<%@ taglib uri=“在web.xml裏面標籤庫配置的uri” prefix=“當引用該標籤庫時使用的前綴” %>。
當directive爲include時,表示將另一個文件的內容全部放在此處,在轉換爲servlet時引入文件內容就成了整個文件的一部分並按照jsp規則解析,格式爲<%@ include file=“文件相對url地址” %>。
當directive爲page時,attribute有很多取值來表示不同的指令:
- import:導入要使用的Java類,多個用逗號分隔或者從新使用個該標籤,如<%@ page import=“java.util.*, java.lang.*” %>
- contentType:指定當前JSP頁面的MIME類型和字符編碼,如<%@ page language=“java” contentType=“text/html;charset=GBK” %>
- pageEncoding:指定當前JSP頁面的字符編碼,如果指定了pageEncoding,就以它爲主,如果不存在,再找contentType的charset,如<%@ page language=“java” contentType=“text/html” pageEncoding=“GBK” %>
- isELIgnored:是否啓用el表達式,默認false,即爲啓用,如<%@ page isELIgnored=“true”%>
- errorPage:指定當JSP頁面發生異常時需要轉向的錯誤處理頁面,如<%@ page errorPage=“error.jsp”%>
- isErrorPage:指定當前頁面是否可以作爲另一個JSP頁面的錯誤處理頁面,<%@ page isErrorPage=“true”%>
- language:指定腳本語言,默認爲java,因爲我們現在只使用java,所以該值不管。
JSP動作
JSP動作的格式爲<jsp:action_name attribute=“value” />,常用的action_name如下:
- include:在_jspService插入一條類似於RequestDispatcher的include語句,格式爲<jsp:include page=“相對URL地址” flush=“true” />
- forward:在_jspService插入一條類似於RequestDispatcher的forward語句,格式爲<jsp:forward page=“相對URL地址” />
- userBean:在_jspService中聲明一個變量,並從作用域中獲取相應的對象賦值給變量,如果在作用於中不存在相應的對象就新建對象並放入相應的作用域,格式爲<jsp:userBean id=“變量名” scope=“page(默認)/request/session/application” class=“具有無參構造函數的帶包類”/>或<jsp:userBean id=“變量名” scope="" class="">這部分jsp內容會在userBean新建的時候執行,如用jsp:setProperty/來進行新建對象的初始化</jsp:userBean>
- setProperty:設置bean的屬性,調用屬性的set方法,會依次從page、request、session、和application作用域尋找bean,格式爲<jsp:setProperty name=“變量名”, property=“屬性名” value=“屬性值”/>
- getProperty:out.write(bean的屬性),調用bean屬性的get方法,會依次從page、request、session、和application作用域尋找bean,格式爲<jsp:getProperty name=“變量名” property=“屬性名” />
JSP表達式
EL表達式中可以有對象、常量、標籤庫中註冊的函數以及運算符。當JSP解析器見到{fn:contains(object.attr, sub)}。
EL支持的運算符有:
- 基本運算:+、-、*、/(div)、%(mod) 加、減、乘、除(除就是除,不是取整)、取模,只適合數字或者由數字組成的字符串
- 邏輯運算:==(eq)、!=(ne)、<(lt)、>(gt)、<=(le)、>=(ge)、&&(and)、||(or)、!(not)
- . 、[] 訪問一個Bean屬性或者一個映射條目,如bean.attr、bean[“attr”]、map.key、map.[“key”],若映射條目有特殊字符只能用[],數組或者鏈表只能用[],如list[1].attr
- ( ) 組織一個子表達式以改變優先級
- ? : 條件運算的三目運算符
- empty 測試是否空值,當爲null、空字符串或空集合時返回true
EL支持的隱藏對象有:pageScope、requestScope、sessionScope、applicationScope、param、paramValues、header、headerValues、initParam、cookie、pageContext。當對象引用不是以隱藏對象開頭的,那麼會依次從四大作用域中去找相應的對象。
JSP標籤與函數
標籤能過通過pageContext獲取很多對象,所以常用來進行過程處理,而函數只能拿到輸入參數並得到處理後的返回值。
函數的定義很簡單,其所在的類不需要實現和繼承任何類,但要求函數必須是public static修飾的公有靜態函數,函數的調用見EL表達式。
JSP標籤對應着一個實現了JspTag的標籤處理類,在JSP引擎解析到JSP標籤時,會在_jspService的第二部分創建標籤對應的處理類,並依次執行其生命週期方法。定義JSP標籤的過程分爲兩步,首先要實現標籤處理類,其實現方式有兩種,分別爲傳統方式和簡單方式,然後需要將處理類進行註冊,註冊到標籤庫文件中。使用標籤時需要先在web.xml中用標籤下的標籤引入標籤庫,然後需要在需要使用標籤的jsp頁面用<%@ taglib/>指令引入標籤庫。
JSP標籤庫在一個xml格式的tld後綴文件中進行標籤與函數的註冊,其格式如下:
<?xml version="1.0" encoding="utf-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<!-- 標籤庫對應的名字,不常用 -->
<display-name>name</display-name>
<!-- 標籤庫的描述,不常用 -->
<description>description</description>
<!-- 指定標籤庫版本 -->
<tlib-version>1.0</tlib-version>
<!-- 在jsp中建議使用的前綴,只是建議 -->
<short-name>myshortname</short-name>
<!-- 指定引用該標籤的uri地址,如果標籤庫指定了該值,那麼在jsp頁面可以直接用該值引入標籤庫,而無需在web.xml中引入該標籤庫,當然也可以在web.xml中引入該標籤庫並指定一個新的uri,在jsp頁面依然可以通過新的uri引入標籤庫 -->
<uri>http://mycompany.com</uri>
<!-- 各個標籤的註冊,可以有多個 -->
<tag>
<!-- tag對應的名字,不常用 -->
<display-name>name</display-name>
<!-- tag的描述,不常用 -->
<description>description</description>
<!-- 標籤的名字,供調用 -->
<name>tagName<name>
<!-- 標籤處理類 -->
<tag-class>com.ejie.MyTag</tag-class>
<!-- 對標籤體的定義,當爲empty時,其內容就算不爲空也會被當做空,當爲JSP時會當中JSP解析出結果,當爲scriptless時,可以爲EL表達式和JSP動作元素不能爲JSP腳本如<% %>與<%= %>等,當爲tagdependent時,不會對內容做任何解析 -->
<body-content>empty/JSP(簡單標籤不支持該值)/tagdependent/scriptless</body-content>
<!-- 未聲明的屬性是否允許使用,默認false,不可以與attribute共存 -->
<dynamic-attributes>true/false</dynamic-attributes>
<!-- 定義標籤可以有的屬性,可以有多個 -->
<attribute>
<!-- 屬性名 -->
<name>att</name>
<!-- s屬性的描述,不常用 -->
<description>description</description>
<!-- 是否必須賦值的屬性,默認false -->
<required>true/false</required>
<!-- 屬性是否支持運行時表達式,如JSP表達式(<%=value%>)和EL表達式(${value}) -->
<rtexprvalue>true/false</rtexprvalue>
<!-- 參數類型 -->
<type>java.lang.String</type>
</<attribute>
</tag>
<function>
<!-- 函數對應的名字,不常用 -->
<display-name>name</display-name>
<!-- 函數的描述,不常用 -->
<description>description</description>
<!-- 函數對應的名字,供調用 -->
<name>funName</name>
<!-- 函數的定義所在類 -->
<function-class>org.apache.taglibs.standard.functions.Functions</function-class>
<!-- 函數的簽名 -->
<function-signature>java.lang.String substringBefore(java.lang.String, java.lang.String)</function-signature>
</function>
</taglib>
JSP標籤處理器的傳統定義方式都是實現了JspTag的子接口Tag,他的生命週期方法依次爲 setPageContext(PageContext pageContext)->setParent(Tag tag)->doStartTag()->doEndTag()->release()。release方法用於釋放資源,在容器關閉時纔會調用。PageContext對象可以用來獲取幾大隱藏對象。當讀到標籤時調用doStartTag方法,返回值爲SKIP_BODY(不會將標籤的內容寫入response的輸出流)與EVAL_BODY_INCLUDE(將標籤內容寫入response的輸出流),當讀到結束標籤時調用doEndTag方法,其返回值可以取EVAL_PAGE(繼續處理標籤後面的頁面)與SKIP_PAGE(該標籤後面的jsp頁面不再處理),一般繼承有Tag默認實現方法的基類BodyTagSupport。
JSP標籤處理器的簡單定義方式都是實現了JspTag的子接口SimpleTag,它是jsp2.0之後引入的,除了部分老的框架在使用傳統定義方式外,都建議使用簡單定義方式。其生命週期方法依次爲setJspContext(JspContext jspContext)->setJspBody(JspFragment jspFragment)->setParent(JspTag jspTag)->doTag()。爲了定義簡單標籤的方便,一般繼承有SimpleTag默認實現方法的基類SimpleTagSupport,幾個set方法都用javabean的方式設置了成員變量並提供了get方法,doTag方法默認爲空,一般繼承自SimpleTagSupport的標籤處理器只需要實現doTag方法,負責解析標籤就可以了。只有該標籤在另一個自定義標籤裏面的時候纔會有父標籤,否則父標籤爲null。
在標籤處理類中定義與屬性名相同的成員變量,並定義其set方法,在doTag中就可以使用這些成員變量來獲取標籤的屬性值。獲取標籤內容的方式爲JspFragment(通過getJspBody獲取)的invoke(Writer writer)方法,當writer傳null的時候,默認爲寫入response的輸出流,如果希望自己處理,可以定義一個StringWriter獲取標籤內容,通過其toString方法獲取標籤內容。如果希望該標籤之後的jsp頁面不再處理,只需要拋出SkipPageException異常。
JSTL
JSP標準標籤庫(JSTL:Java Standard Tag Library)是一個JSP標籤集合,它封裝了JSP應用的通用核心功能,使用時需要引入相應的standard包和jstl包。主要的標籤庫有核心標籤庫、格式化標籤、SQL標籤與XML標籤以及JSTL函數。
標準庫裏最常用的就是核心包,引用方式<%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core” %>,其標籤如下:
- if,條件判斷,如同代碼中的if,格式爲<c:if test=“條件判斷” var=“保存判斷的結果” scope=“結果保留的作用域”>…</c:if>示例如下:
<c:if test="${booleanA}" var="result" scope="page">
<p>booleanA = true<p>
</c:if>
<c:if test="${result}">
<p>booleanA = false<p>
</c:if>
- choose、when與otherwise,當某個when成立的時候,後面的when和otherwise都無用了,如同代碼中的swatch、case與default。
<c:choose>
<c:when test="${booleanA}">
<p>A</p>
</c:when>
<c:when test="${booleanB}">
<p>B</p>
</c:when>
<c:otherwise>
<p>都不是</p>
</c:otherwise>
</c:choose>
- forEach,循環,格式爲<c:forEach items=“被遍歷的集合對象,默認爲一個由begin-end的連續整數集合” begin=“遍歷的起始位置,默認爲0” end=“遍歷的結束位置,默認爲最後一個元素下標” step=“遍歷的步長,默認1” var=“保存遍歷出來的當前對象” varStatus=“可以通過該對象獲取如當前的索引index、第幾次進入循環count”>…</c:foreach>。
<c:forEach items="list" var="item" begin="6" end="15" step="2" varStatus="status">
<p>${item}</p>
<p>${status.count}</p>
<p>${status.index}</p>
</c:forEach>
- forTokens,循環,與forEach相似,只是items爲用符號分割的元素,因此比forEach多一個屬性delims來指定分割符號。
<c:forEach items="item1,item2,item3" var="item" begin="6" end="15" step="2" varStatus="status" delims=",">
<p>${item}</p>
<p>${status.count}</p>
<p>${status.index}</p>
</c:forEach>
</c:forEach>
- redirect,重定向,如<c:redirect url=“url”/>
- out,在jsp中顯示數據,如<c:out value=“輸出” default=“當value通過EL表達式取值爲null時的默認值,該屬性的默認值爲該標籤的內容” escapeXml=“true/false”/>
- set,設置變量值或某個變量的屬性值,格式爲<c:set var=“變量名” value=“變量值” scope=""/>或<c:set target=“對象名” property=“屬性名” value=“變量值值” scope=""/>
- catch,異常捕獲<c:catch var =“catchException”>可能發生異常的部分</c:catch>,當異常發生時異常被捕獲catchException
標準庫裏還常用的就是函數,引用方式<%@ taglib prefix=“fn” uri=“http://java.sun.com/jsp/jstl/functions”%>,其標籤如下:
- trim(),移除首尾的空白符,public static String trim(String input)
- length(),返回字符串長度,public static int length(Object obj)
- replace(),將輸入字符串中指定的位置替換爲指定的字符串然後返回,public static String replace(String input, String substringBefore, String substringAfter)
- contains(),測試輸入的字符串是否包含指定的子串,public static boolean contains(String input, String substring)
- containsIgnoreCase(),測試輸入的字符串是否包含指定的子串,大小寫不敏感,public static boolean containsIgnoreCase(String input, String substring)
- substring(),返回字符串的子集,public static String substring(String input, int beginIndex, int endIndex)
- substringBefore(),返回字符串在指定子串之前的子集,public static String substringBefore(String input, String substring)
- substringAfter(),返回字符串在指定子串之後的子集,public static String substringAfter(String input, String substring)
- indexOf(),返回指定字符串在輸入字符串中出現的位置,public static int indexOf(String input, String substring)
- startsWith(),測試輸入字符串是否以指定的前綴開始,public static boolean startsWith(String input, String substring)
- endsWith(),測試輸入的字符串是否以指定的後綴結尾,public static boolean endsWith(String input, String substring)
- join(),將數組中的元素合成一個字符串然後輸出,public static String join(String[] array, String separator)
- split(),將字符串用指定的分隔符分隔然後組成一個子字符串數組並返回,public static String[] split(String input, String delimiters)
- toLowerCase(),將字符串中的字符轉爲小寫,public static String toLowerCase(String input)
- toUpperCase(),將字符串中的字符轉爲大寫,public static String toUpperCase(String input)
JSP + Servlet
用JSP作爲顯示層,Servlet作爲控制層,請求來到Servlet,然後Servlet將處理好的數據放在請求的attribute中,JSP從請求的attribute中獲取數據顯示。