JSP標準標簽庫(JSTL)是一個JSP標簽集合,它封裝了JSP應用的通用核心功能。
JSTL支持通用的、結構化的任務,比如迭代,條件判斷,XML文檔操作,國際化標簽,SQL標簽。 除了這些,它還提供了一個框架來使用集成JSTL的自定義標簽。
根據JSTL標簽所提供的功能,可以將其分為5個類別。
核心標簽
格式化標簽
SQL 標簽
XML 標簽
JSTL 函數
JSTL 庫安裝
Apache Tomcat安裝JSTL 庫步驟如下:
從Apache的標準標簽庫中下載的二進包(jakarta-taglibs-standard-current.zip)。
官方下載地址:http://archive.apache.org/dist/jakarta/taglibs/standard/binaries/
本站下載地址:jakarta-taglibs-standard-1.1.2.zip
下載jakarta-taglibs-standard-1.1.2.zip 包并解壓,將jakarta-taglibs-standard-1.1.2/lib/下的兩個jar文件:standard.jar和jstl.jar文件拷貝到/WEB-INF/lib/下。
接下來我們在 web.xml 文件中添加以下配置:
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.4"
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-app_2_4.xsd">
<jsp-config>
<taglib>
<taglib-uri>http://java.sun.com/jstl/fmt</taglib-uri>
<taglib-location>/WEB-INF/fmt.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/fmt-rt</taglib-uri>
<taglib-location>/WEB-INF/fmt-rt.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/core</taglib-uri>
<taglib-location>/WEB-INF/c.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/core-rt</taglib-uri>
<taglib-location>/WEB-INF/c-rt.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/sql</taglib-uri>
<taglib-location>/WEB-INF/sql.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/sql-rt</taglib-uri>
<taglib-location>/WEB-INF/sql-rt.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/x</taglib-uri>
<taglib-location>/WEB-INF/x.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jstl/x-rt</taglib-uri>
<taglib-location>/WEB-INF/x-rt.tld</taglib-location>
</taglib>
</jsp-config></web-app>
使用任何庫,你必須在每個JSP文件中的頭部包含<taglib>標簽。
核心標簽
核心標簽是最常用的JSTL標簽。引用核心標簽庫的語法如下:
<%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
標簽 | 描述 |
---|---|
<c:out> | 用于在JSP中顯示數據,就像<%= ... > |
<c:set> | 用于保存數據 |
<c:remove> | 用于刪除數據 |
<c:catch> | 用來處理產生錯誤的異常狀況,并且將錯誤信息儲存起來 |
<c:if> | 與我們在一般程序中用的if一樣 |
<c:choose> | 本身只當做<c:when>和<c:otherwise>的父標簽 |
<c:when> | <c:choose>的子標簽,用來判斷條件是否成立 |
<c:otherwise> | <c:choose>的子標簽,接在<c:when>標簽后,當<c:when>標簽判斷為false時被執行 |
<c:import> | 檢索一個絕對或相對 URL,然后將其內容暴露給頁面 |
<c:forEach> | 基礎迭代標簽,接受多種集合類型 |
<c:forTokens> | 根據指定的分隔符來分隔內容并迭代輸出 |
<c:param> | 用來給包含或重定向的頁面傳遞參數 |
<c:redirect> | 重定向至一個新的URL. |
<c:url> | 使用可選的查詢參數來創造一個URL |
格式化標簽
JSTL格式化標簽用來格式化并輸出文本、日期、時間、數字。引用格式化標簽庫的語法如下:
<%@ taglib prefix="fmt"
uri="http://java.sun.com/jsp/jstl/fmt" %>
標簽 | 描述 |
---|---|
<fmt:formatNumber> | 使用指定的格式或精度格式化數字 |
<fmt:parseNumber> | 解析一個代表著數字,貨幣或百分比的字符串 |
<fmt:formatDate> | 使用指定的風格或模式格式化日期和時間 |
<fmt:parseDate> | 解析一個代表著日期或時間的字符串 |
<fmt:bundle> | 綁定資源 |
<fmt:setLocale> | 指定地區 |
<fmt:setBundle> | 綁定資源 |
<fmt:timeZone> | 指定時區 |
<fmt:setTimeZone> | 指定時區 |
<fmt:message> | 顯示資源配置文件信息 |
<fmt:requestEncoding> | 設置request的字符編碼 |
SQL標簽
JSTL SQL標簽庫提供了與關系型數據庫(Oracle,MySQL,SQL Server等等)進行交互的標簽。引用SQL標簽庫的語法如下:
<%@ taglib prefix="sql"
uri="http://java.sun.com/jsp/jstl/sql" %>
標簽 | 描述 |
---|---|
<sql:setDataSource> | 指定數據源 |
<sql:query> | 運行SQL查詢語句 |
<sql:update> | 運行SQL更新語句 |
<sql:param> | 將SQL語句中的參數設為指定值 |
<sql:dateParam> | 將SQL語句中的日期參數設為指定的java.util.Date 對象值 |
<sql:transaction> | 在共享數據庫連接中提供嵌套的數據庫行為元素,將所有語句以一個事務的形式來運行 |
XML 標簽
JSTL XML標簽庫提供了創建和操作XML文檔的標簽。引用XML標簽庫的語法如下:
<%@ taglib prefix="x"
uri="http://java.sun.com/jsp/jstl/xml" %>
在使用xml標簽前,你必須將XML 和 XPath 的相關包拷貝至你的<Tomcat 安裝目錄>\lib下:
XercesImpl.jar
下載地址: http://www.apache.org/dist/xerces/j/
xalan.jar
下載地址: http://xml.apache.org/xalan-j/index.html
標簽 | 描述 |
---|---|
<x:out> | 與<%= ... >,類似,不過只用于XPath表達式 |
<x:parse> | 解析 XML 數據 |
<x:set> | 設置XPath表達式 |
<x:if> | 判斷XPath表達式,若為真,則執行本體中的內容,否則跳過本體 |
<x:forEach> | 迭代XML文檔中的節點 |
<x:choose> | <x:when>和<x:otherwise>的父標簽 |
<x:when> | <x:choose>的子標簽,用來進行條件判斷 |
<x:otherwise> | <x:choose>的子標簽,當<x:when>判斷為false時被執行 |
<x:transform> | 將XSL轉換應用在XML文檔中 |
<x:param> | 與<x:transform>共同使用,用于設置XSL樣式表 |
JSTL函數
JSTL包含一系列標準函數,大部分是通用的字符串處理函數。引用JSTL函數庫的語法如下:
<%@ taglib prefix="fn"
uri="http://java.sun.com/jsp/jstl/functions" %>
函數 | 描述 |
---|---|
fn:contains() | 測試輸入的字符串是否包含指定的子串 |
fn:containsIgnoreCase() | 測試輸入的字符串是否包含指定的子串,大小寫不敏感 |
fn:endsWith() | 測試輸入的字符串是否以指定的后綴結尾 |
fn:escapeXml() | 跳過可以作為XML標記的字符 |
fn:indexOf() | 返回指定字符串在輸入字符串中出現的位置 |
fn:join() | 將數組中的元素合成一個字符串然后輸出 |
fn:length() | 返回字符串長度 |
fn:replace() | 將輸入字符串中指定的位置替換為指定的字符串然后返回 |
fn:split() | 將字符串用指定的分隔符分隔然后組成一個子字符串數組并返回 |
fn:startsWith() | 測試輸入字符串是否以指定的前綴開始 |
fn:substring() | 返回字符串的子集 |
fn:substringAfter() | 返回字符串在指定子串之后的子集 |
fn:substringBefore() | 返回字符串在指定子串之前的子集 |
fn:toLowerCase() | 將字符串中的字符轉為小寫 |
fn:toUpperCase() | 將字符串中的字符轉為大寫 |
fn:trim() | 移除首位的空白符 |
如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!
本系列文章旨在記錄和總結自己在Java Web開發之路上的知識點、經驗、問題和思考,希望能幫助更多(Java)碼農和想成為(Java)碼農的人。
上篇文章我們使用JSP技術對租房網平臺進行了改造,也提到下面這樣的代碼有點奇葩:
<%for (House house : mockHouses) { System.out.println(house); %> <li><h2><a href="house-details.jsp?userName=${param.userName}&houseId=<%=house.getId() %>"><%=house.getName() %></a></h2></li> <%} %>
像這樣的代碼我們可以使用JSTL技術來解決。當然,JSTL可不僅僅只有這點功能,你還可以定義自己的標簽。
在這篇文章里,我提到過JSTL是JSP相關的技術,從它的名字全稱(JSP Standard Tag Library,即JSP標準標簽庫)就可以看出來。
在本篇文章里,我們就嘗試初步使用JSTL來進一步改造我們的租房網應用。
既然稱之為庫,那么它包含哪些庫呢?
我們可以把JSTL規范下載下來看一下,當然你也可以找本相關書籍,或者直接在網上搜索一下。
JSTL規范的下載類似Servlet規范的下載(可以參考這篇文章),不過我們在JCP官網(https://jcp.org/en/home/index)中搜索的關鍵字就變成Tag或Tag Library了。
我們在搜索結果中就可以看到:
點擊 Download page 鏈接可以看到:
然后繼續點擊底部的 Maintenance Review 2 of JSR 52 鏈接,跳轉到:
不過,再次點擊DOWNLOAD按鈕時,我這邊出現無法訪問此網站的錯誤。所以我轉而點擊底部的 JSR-000052 A Standard Tag Library for JavaServer Pages Detail Page 鏈接:
從這里我們可以看到JSTL規范的各個階段,我們選擇 Maintenance Release 2 這個階段,點擊它右邊的 Download page 鏈接:
然后再點擊紅色箭頭所指的鏈接,跳轉到真正的下載頁面:
后續操作就跟Servlet規范的下載類似了。
現在,我們可以打開JSTL規范,可以看到如下描述:
事實上,JSTL應該就只是一個庫,但它根據不同功能而劃分成了多個庫:
JSTL的終極目標是簡化JSP頁面的開發,所以,它應該是在JSP頁面中使用。
既然是標簽,那它的使用是否跟HTML標簽、XML標簽類似呢?答案是肯定的。
不過,JSTL標簽的使用與XML標簽使用時聲明命名空間類似,也需要告訴Servlet/JSP容器該JSP頁面需要引入某個庫(即上述的核心、XML處理、國際化、數據庫訪問、函數等等,以及以后自定義的標簽)。
在JSP頁面中是使用一個JSP指令(即taglib指令,之前我們用過page指令)來聲明的:
<%@ taglib uri="uri" prefix="prefix" %>
舉個例子,假設我們要使用JSTL的核心庫,則應該在JSP頁面的開頭處這樣聲明:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
然后,就可以使用核心庫的標簽,比如out標簽:
<c:out value="value" [escapeXml="{true|false}"] [default="defaultValue"]/> <c:out value="value" [escapeXml="{true|false}"]> default value </c:out>
注意:在標簽的語法中,[]表示可選的屬性。如果值帶下劃線,則表示為默認值。
out標簽有兩種形式,有屬性和屬性值,也可能有標簽內容,跟HTML標簽和XML標簽類似。
我們就拿租房網應用中的房源列表頁面houses.jsp來使用JSTL改造,因為這里涉及列表數據的展示。
列表數據是很常見的,我們經常可以看到包含列表數據的頁面,比如訂單列表、商品列表等等。
houses.jsp原來的代碼是這樣的:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="include.jsp"%> <% List<House> mockHouses = (List<House>) request.getAttribute("mockHouses"); System.out.println(mockHouses); %> <h6>共找到你感興趣的房源 <%=mockHouses.size() %> 條</h6> <ul> <%for (House house : mockHouses) { System.out.println(house); %> <li><h2><a href="house-details.jsp?userName=${param.userName}&houseId=<%=house.getId() %>"><%=house.getName() %></a></h2></li> <%} %> </ul> </body> </html>
首先要引入JSTL中的核心庫(因為我們后面要用到的forEach標簽是屬于核心庫的):
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="include.jsp"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
遺憾的是,我們添加這句聲明之后,Eclipse就報錯了:
提示是說找不到這個標簽庫的描述符。那就根據這個提示在網上搜索答案唄。
答案就是原來我們還需要在我們的工程結構里配置JSTL相關的JAR包,而Tomcat這個Servlet/JSP容器本身不提供這些JSTL相關的JAR包,我們需要單獨下載。
那么在哪里下載呢?既然我們是使用Tomcat這個Servlet/JSP容器,那么我們就可以到它的官網(http://tomcat.apache.org/)上看看:
我們可以看到左側導航欄中有個Taglibs鏈接,我們點進去看看:
還真就是JSTL相關JAR包的下載頁面,點擊Download鏈接進去,可以找到真正的下載鏈接:
我們可以看下Binary README文件,里面有該JAR包版本支持的Tomcat和JSP等版本信息,如果我們使用的Tomcat是最新版本(9.0.x),那直接下載下面四個JAR包即可,否則需要點進Archives頁面下載歷史版本。
好,我們把四個JAR包下載下來之后,就可以添加到我們的租房網應用的工程里面了,可以參考這篇文章。
實際上,直接把這四個JAR包拷貝到WebContent/WEB-INF/lib節點下是最快的。
現在,我們可以看到上述的Eclipse報錯提示就消失了。
首先,我們可以使用EL表達式來訪問列表的長度:
<h6>共找到你感興趣的房源 ${mockHouses.size()} 條</h6>
據說低版本的JSP容器不可以這樣直接用EL表達式來訪問列表的長度,而應該使用JSTL的函數庫中的length方法:
${fn:length(list) }
當然,首先需要引入JSTL的函數庫。讀者朋友可以自行嘗試一下。
我們可以通過JSTL規范查看一下這個forEach標簽的用法:
當然,可能只看這個語法格式還是不太明白如何使用,那么可以繼續看規范中的描述,或者直接網上搜索即可。
我們需要改造的代碼是這一部分:
<%for (House house : mockHouses) { System.out.println(house); %> <li><h2><a href="house-details.jsp?userName=${param.userName}&houseId=<%=house.getId() %>"><%=house.getName() %></a></h2></li> <%} %>
我們可以先把它注釋掉(在Eclipse中可以選中這一段,然后鍵入Ctrl + Shift + /),然后敲入開始標簽的左尖括號 < ,于是Eclipse會出現智能提示:
嗯,這個功能還是挺好用的,提高開發效率。
閑話不多說了,直接上代碼:
<c:forEach var="house" items="${mockHouses}"> <li><h2><a href="house-details.jsp?userName=${param.userName}&houseId=${house.id}">${house.name}</a></h2></li> </c:forEach>
看似跟之前變化不大,但至少沒那么奇葩了,風格也與HTML很一致。
forEach標簽遍歷列表數據的基本屬性是:
forEach標簽的內容就可以是普通的HTML標簽了,然后HTML標簽里我們就可以使用EL表達式來訪問列表中每一項數據。
實際上,EL表達式直接訪問對象的屬性,而非調用對象的方法,比如:
${house.id}
注意,param是EL的隱式對象,它是由Servlet/JSP容器創建并傳進來的,它可以直接訪問請求所攜帶的參數。
所以,我們實際上可以把原來的這一部分:
<% List<House> mockHouses = (List<House>) request.getAttribute("mockHouses"); System.out.println(mockHouses); %>
刪除掉。
現在houses.jsp的代碼就變成這樣了:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="include.jsp"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <h6>共找到你感興趣的房源 ${mockHouses.size()} 條</h6> <ul> <c:forEach var="house" items="${mockHouses}"> <li><h2><a href="house-details.jsp?userName=${param.userName}&houseId=${house.id}">${house.name}</a></h2></li> </c:forEach> </ul> </body> </html>
是不是清晰明朗了許多?
當然,JSP頁面的開發者必須知道傳進這個頁面的到底有哪些數據對象!
沒錯,這就相當于把數據的展示(視圖層)給分離開來,你需要與后端(控制器層和模型層)約定/設計好每一個JSP頁面都有哪些數據對象。
然后,JSP頁面的開發者和后端(控制器層和模型層)的開發者就可以各自獨立去開發了。
剩下的house-details.jsp和house-form.jsp該如何改造呢?大家可以先思考一下。
提示:JSP頁面應該只關心取數據展示,而不應該關心如何查找到某個數據這種邏輯,應該把這種邏輯放到后端(控制器層和模型層)。
我的改造是這樣的,把原來這兩個頁面的如何查找到某個數據這種邏輯放到Filter中,前后端約定好這兩個頁面中存在target這個House對象,于是doFilter()方法中在將請求交給下個節點之前應該掛載上target這個House對象:
if (userName == null || userName.isEmpty()) { System.out.println("invalid user!"); httpServletResponse.sendRedirect("login.html"); } else { String houseId = httpServletRequest.getParameter("houseId"); if (houseId != null && !houseId.trim().isEmpty()) { House target = findHouseById(houseId);//找不到怎么辦? httpServletRequest.setAttribute("target", target); } chain.doFilter(request, response); }
另外,應該把如何查找某個房源的邏輯封裝起來:
private House findHouseById(String houseId) { for (House house : mockHouses) { if (houseId.equals(house.getId())) { return house; } } return null; }
然后,這兩個頁面就極其簡單了,house-details.jsp是這樣的:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="include.jsp"%> <h2>${target.name}<a href="house-form.jsp?userName=${param.userName}&houseId=${target.id}">編輯</a></h2> <h3>${target.detail}</h3> <h4><a href="houses.jsp?userName=${param.userName}">回到列表</a></h4> </body> </html>
house-form.jsp是這樣的:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="include.jsp"%> <form action="house-form.servlet" method="post"> <input type="hidden" name="userName" value="${param.userName}"/> <input type="hidden" name="houseId" value="${target.id}"/> <label for="house_name">房源名字:</label><input type="text" id="house_name" name="houseName" value="${target.name}" /> <label for="house_detail">房源詳細信息:</label><input type="text" id="house_detail" name="houseDetail" value="${target.detail}" /> <input type="submit" value="提交" /> </form> </body> </html>
可以看到,這兩個頁面只是取數據(target這個House對象)展示而已。
然JSTL提供了很多豐富的標簽,但是在某些情況下,這些標簽還是不能夠滿足我們的業務需求,所以sun公司也提供了自定義標簽的功能,可以根據實際業務需求創建相應的標簽。自定義標簽在一些很老的項目中會經常見到,因為那個時候基本上是基于Servlet和JSP進行項目的開發,為了應對自身的項目需求,每個公司都會自己創建自己的標簽庫,從而簡化開發,提高開發效率。
自定義JSTL標簽只需要下面幾個步驟即可:
創建一個CustomTag類,繼承自SimpleTagSupport類,重寫doTag()方法。
package com.gitcode.tag;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.io.StringWriter;
/**
* 自定義JSTL標簽
*/
public class CustomTag extends SimpleTagSupport {
/**
* 自定義標簽屬性
*/
private String content;
/**
* 標簽體內容
*/
private StringWriter body = new StringWriter();
public void setContent(String content) {
this.content = content;
}
@Override
public void doTag() throws JspException, IOException {
// 1、獲取輸出流對象
JspWriter writer = this.getJspContext().getOut();
if (content == null) {
// 屬性等于空,則直接獲取標簽之間的body內容
JspFragment jspBody = this.getJspBody();
// 調用方法
jspBody.invoke(body);
// 輸出內容
writer.println(body.toString());
} else {
// 如果屬性名稱不為空,則輸出屬性名稱的內容
writer.println(content);
}
}
}
在WEB-INF目錄下,我們創建一個tld目錄,用于保存我們自定義JSTL標簽的tld描述文件,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">
<tlib-version>1.1</tlib-version>
<short-name>custom_tag</short-name>
<tag>
<description>自定義JSTL標簽的描述內容</description>
<name>custom_tag</name>
<tag-class>com.gitcode.tag.CustomTag</tag-class>
<!-- 標簽之間的內容是腳本 -->
<body-content>scriptless</body-content>
<attribute>
<description>屬性的描述內容</description>
<name>content</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
如下圖所示:
新建一個custom.jsp頁面,使用<%@taglib%>指令引入自定義標簽,如下所示:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%-- 引入標簽庫 --%>
<%@ taglib prefix="custom_tag" uri="WEB-INF/tld/custom_tag.tld" %>
<html>
<head>
<title>JSTL標簽庫之自定義標簽</title>
</head>
<body>
<h3>JSTL標簽庫之自定義標簽</h3>
<custom_tag:custom_tag content="content屬性內容"></custom_tag:custom_tag>
<h3>JSTL標簽庫之自定義標簽</h3>
<custom_tag:custom_tag>
<ul>
<li>第一行</li>
<li>第二行</li>
<li>第三行</li>
</ul>
</custom_tag:custom_tag>
</body>
</html>
運行結果如下所示:
需要注意的是,prefix屬性值必須和custom_tag.tld描述文件中的<short-name>標簽值保持一致,否則無法引用自定義標簽。
以上,就是JSTL自定義標簽的使用,創建自定義標簽可以將公用的代碼封裝起來,后續要使用的時候,可以減少代碼的開發,從而提高開發效率。
今天就到這里,未完待續~~
*請認真填寫需求信息,我們會在24小時內與您取得聯系。