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小时内与您取得联系。