视图层JSP中使用JSTL核心标签
在使用jsp作为视图技术的时候,我们会在页面中书写大量的java代码,这样其实不是非常的友好。在MVC的架构中,我们的目的是希望将模型层和视图层分开,视图层只用来做数据展示。由于视图层的本质就是HTML,而HTML则是标签化语言,因此很多模板技术也都使用”特殊标签"来展示数据,这些标签的特殊在于,它并不是HTML语言的范围,而是完全由模板开发者自定义的。因此,它不能在浏览器上正确执行,需要翻译成java代码,然后才能向浏览器输出最终的html内容。由此可见,模板技术的使用是为了方便开发者而创建的,目的是改变html和java代码”混编“的方式,让整个项目工程代码更加清晰。既然模板技术有这样的优势,那么jsp官方也就出品了这样的标签库,我们称之为JSTL(JSP标准标签库)。JSP 标签是一组与 HTML 标签相似,但又比 HTML 标签强大的功能标签。JSTL 用来简化 JSP 开发,可以使我们不用嵌入 Java 代码就能够开发出复杂的 JSP 页面。
我们可以从Apache官网的标准标签库中下载的二进包,地址为:https://archive.apache.org/dist/jakarta/taglibs/standard/binaries/
我们选择 “jakarta-taglibs-standard-1.1.2.zip” 进行下载就行啦。下载完毕后解压缩后在lib目录下就会发现两个JAR文件:“jstl.jar” 和 “standard.jar”,如下所示:
JSTL 包含 5 类标签库:core 标签库、fmt 标签库、fn 标签库、XML 标签库和 SQL 标签库。这 5 类标签库基本覆盖了 Web 开发中的所涉及的技术展示。 本章节我们只介绍core 标签库的使用,我们继续使用SpringMVCDemo工程。首先两个jar文件:jstl.jar和standard.jar文件复制到项目的WEB-INF/lib/目录下。
在使用标签库之前,我们必须在JSP页面的顶部使用<%@ taglib %>指令定义引用的标签库和访问前缀。使用核心标签库的taglib指令格式如下:
<%@ taglib prefix=”c” uri=”http://java.sun.com/jsp/jstl/core” %>
核心标签库主要用于完成JSP页面的常用功能,包括表达式标签,URL标签,流程控制标签和循环标签共4种标签。
表达式标签包括 <c:out>,<c:set>,<c:remove>和<c:catch>
URL标签库包括 <c:import>,<c:redirect>,<c:url>和<c:param>
流程控制标签包括 <c:if>,<c:choose>,<c:when>和<c:otherwise>
循环标签包括 <c:forEach>和<c:forTokens>
接下来,我们就注意介绍这些标签如何使用。
首先是 <c:out> 将表达式的值输出到页面,相当于JSP表达式 <%= 表达式 %>
语法格式:<c:out value="<string>" default="<string>" escapeXml="<true|false>"/>
value就是要输出的内容 ,default 是输出的默认值,escapeXml 表示是否忽略XML特殊字符,其中后两项可以没有。如果我们要输出上个章节中helloController传递给hell.jsp中的message信息的话,可以这么写。
<h1>${message}</h1>
<h2><c:out value="${message}" /></h2>
这里,我们仍然要借助EL表达式来获取变量message的内容。如果直接在value中写message的话,<c:out>标签只会原样输出字符串"message",并不会将其作为一个变量使用。如果只是简单的显示变量值的话,可以直接使用EL表达式,而不使用该标签,这样更加简洁。我们运行当前工程,查看hello.jsp页面显示效果
接下来是<c:set> 在指定范围内定义变量
语法格式:<c:set var="<string>" value="<string>" scope="<string>"/>
var是变量的名称,value 是变量的值 ,scope 是变量的作用域。我们可以使用<c:set>设置一个新变量,然后使用<c:out>输出这个新变量,使用方式如下:
<c:set var="name" value="张三" />
<p><c:out value="${name}" /></p>
接下来是<c:remove> 从指定的jsp范围种移除指定的变量。
语法格式:<c:remove var="<string>" scope="<string>"/>
var就是变量的名称,scope就是变量的作用域。那么变量的作用域到底是什么呢?在之前的JSP课程中,我们应该涉及过作用域这一块的内容。这里的作用域主要是page, request, session, application四种,范围也逐渐增大。page代表该变量只在当前页面有效,request表示该变量在当前请求范围内有效,session表示该变量在会话范围内有效,application表示该变量在整个应用范围内有效。作用域影响的是变量的生命周期,也就是变量在什么时候被销毁掉。<c:set>标签默认的作用域是page,也就是说,页面改变后,其定义的变量就会被销毁。关于这个标签,我们就不在演示了。因为在大部分情况中,我们都不会在JSP页面中设置新的变量。即便会这样做,这个新的变量也基本上是page作用域,我们也没有必要显式的使用<c:remove>去销毁它(让它自生自灭即可)。
接下来是<c:catch> 捕获异常,相当于try…catcha语句。
语法格式:<c:catch var="<string>">...</c:catch>
我们一般不会在JSP页面中进行异常捕获,所以很少使用该标签。
接下来介绍<c:import> 引入其他动态或静态页面,相当于<jsp:include>行为标签所具有的功能。
语法格式:<c:import url="<string>" var="<string>" scope="<string>" />
url 就是导入资源的URL,可以是相对路径和绝对路径;var 用于存储资源内容的文本的变量;scope 是变量的的作用域。接下来,我们就来使用这个标签引入另一个jsp文件试试。首先,创建一个 "part.jsp",内容如下
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<p>这里只是一部分内容</p>
没错,这个文件不是一个完整的HTML文档,里面就是一个html片段而已。我们要将它引入到hello.jsp里面
<c:import url="part.jsp" />
我们发现,它和<jsp:include>非常的相似,运行项目后效果
接下来就是 <c:redirect> 标签通过自动重写URL来将浏览器重定向至一个新的URL。
语法格式:<c:redirect url="<string>" />
不用详细介绍了,url就是重定向的地址。
一般情况下,我们不会直接在jsp页面进行重定向的操作。
接下来就是<c:url> 构造一个URL,我们可以将它指定给超链接的href属性使用。
语法格式:<c:url value="<string>" var="<string>" scope="<string>" />
其中value就是url的内容,var和scope是存储url的变量,可以不使用。
<a href="<c:url value='hello.do' />">test</a>
最终会输出的内容为
<a href="hello.do">test</a>
我们发现没有太大的差距,还不如直接写“hello.do”来的直接呢。
接下来是<c:param> 为其他标签提供参数,可以配置上面的<c:url>一起使用,构造有参数的url。
语法格式:<c:param name="<string>" value="<string>"/>
我们的参数基本的都是K-V的形式,name就是K,value就是V, 非常的简单,具体使用方式如下
<c:url value="hello.do" var="link">
<c:param name="name" value="zhangsan" />
<c:param name="age" value="30" />
</c:url>
<a href="${link}">test</a>
我们使用<c:url>生成的链接放入到变量link中,然后在超链接中使用EL表达式读取这个link变量,最终我们的超链接的地址就是如下这样。
<a href="hello.do?name=zhangsan&age=30">test</a>
我们发现这没有太大的差距,还不如直接拼接url来的方便和快捷。
接下来,我们继续介绍<c:if> 分支判断,相当于if语句,没有else标签。
语法格式:<c:if test="<boolean>" var="<string>" scope="<string>">...</c:if>
其中test就是条件表达式,它的结果应该是一个布尔值;var和scope是存储布尔值的变量而已,可以不写。这个<c:if>标签虽然简单,但是会经常的大量的使用。因此,我们必须熟练使用这个标签。举一个简单的例子,我们在Controller控制器层来处理数据,然后传递给jsp页面上,如果数据数据出现问题,我们的jsp页面可能直接崩溃掉。在我们本章节中,我们的HelloController向hello.jsp传递的message数据变量可能是一个空值,虽然它不会让jsp页面崩溃,但是可能对访问的用户产生不好的体验。因为,页面上会输出一个空白。我们可以修改一下
<div>
<c:if test="${message eq ''}">空</c:if>
<c:if test="${message ne ''}">${message}</c:if>
</div>
然后,我们在调整一下HelloController里面的代码
// TODO Auto-generated method stub
ModelAndView mv = new ModelAndView();
mv.addObject("message", "");
//mv.addObject("message", "hello,springmvc!");
//mv.addObject("message", helloDao.getMessage());
mv.setViewName("/hello.jsp");
return mv;
我们运行查看结果
这个分支标签的缺陷在于没有else的部分,有时候使用情况不是很方便。 关于分支判断,还有<c:choose> , <c:when> 和 <c:otherwise> 三个, 大家可以自己研究如何使用。
接下来,我们重点说说<c:forEach> 循环标签的使用,它也是经常的大量的使用,可以遍历数组和集合。
forEach 语法格式:<c:forEach items="<object>" var="<string>" varStatus="<string>" begin="<int>" end="<int>" step="<int>">......</c:forEach>
其中 items 就是要被循环的数组或集合,var代表循环的每一个元素的变量名称,varStatus 代表循环状态的变量名称,beign,end和step分别代表计数循环开始值,结束值以及步长。我们还是以实际案例说明该标签的使用。
<c:forEach var="i" begin="1" end="5">
<c:out value="${i}" />
</c:forEach>
以上循环就是输出1,2,3,4,5数字,属于计数型循环,使用相对来说比较少,我们可以查看运行结果。
接下来,我们演示如何读取数组和列表,首先我们需要从Controller传递过来数组和列表数据,如下所示
// TODO Auto-generated method stub
ModelAndView mv = new ModelAndView();
mv.addObject("message", "");
//mv.addObject("message", "hello,springmvc!");
//mv.addObject("message", helloDao.getMessage());
int arr[] = {1,3,5,7,9};
mv.addObject("arr", arr);
List<String> list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
mv.addObject("list", list);
mv.setViewName("/hello.jsp");
return mv;
我们分别向hello.jsp页面传递了arr数组和list列表两种数据,接下来就需要在hello.jsp中读取这两种数据。
<div>
<c:forEach items="${arr}" var="item">
<span>${item}</span>
</c:forEach>
</div>
<div>
<c:forEach items="${list}" var="item">
<span>${item}</span>
</c:forEach>
</div>
我们运行工程,查看效果
其实对于数组和列表中的元素,还可以更加复杂一下,比如就是一个java类。我们依然可以使用上面的方式读取到。只不过元素item就会对应上这个java类,我们可以通过EL表达式来读取类属性变量,例如 ${item.name}
这里,我们使用一个Map对象来说明上面的问题。同样,我们调整HelloController中的数据传递。
List<Map<String,String>> map = new ArrayList();
Map map1 = new HashMap();
map1.put("name", "张三");
map1.put("age", 30);
map.add(map1);
Map map2 = new HashMap();
map2.put("name", "李四");
map2.put("age", 31);
map.add(map2);
接下来,我们来读取上面的内容
<div>
<c:forEach items="${map}" var="item">
<span>${item.name} ${item.age}</span>
</c:forEach>
</div>
我们运行工程,查看效果
接下来说说<c:forEach> 中的 varStatus 属性,它包含如下值
current 当前这次迭代的(集合中的)项
index 当前这次迭代从 0 开始的迭代索引
count 当前这次迭代从 1 开始的迭代计数
first 用来表明当前这轮迭代是否为第一次迭代的标志
last 用来表明当前这轮迭代是否为最后一次迭代的标志
begin 属性值
end 属性值
step 属性值
这个属性有时候,对我们帮助很大,比如我们在页面上使用列表来展示集合数据的时候,需要做隔行变色的效果。也就是说,当循环元素的下标是偶数的时候,我们可以设置它的背景色,这样就与奇数的元素区分开了。
<div>
<c:forEach items="${map}" var="item" varStatus="status">
<c:if test="${status.count % 2 eq 0}">
<span style="color:red">${item.name} ${item.age}</span>
</c:if>
<c:if test="${status.count % 2 eq 1}">
<span>${item.name} ${item.age}</span>
</c:if>
</c:forEach>
</div>
我们让偶数的数据显示红色,运行工程效果如下
最后我们说一下<c:forTokens> 可以分割字符串的循环标签,我们直接使用案例说明吧。
<div>
<c:forTokens items="hello:java:jsp:spring" delims=":" var="item">
<p>${item}</p>
</c:forTokens>
</div>
上面的代码其实就是按照冒号来分割字符串,然后输出每一个分割元素,运行工程效果如下
如果想使用变量读取字符串的话,可以这样写
<div>
<c:set var="str" value="hello:java:jsp:spring" />
<c:forTokens items="${str}" delims=":" var="item">
<p>${item}</p>
</c:forTokens>
</div>
效果是一样的,不在给出截图了。
整体项目下载地址: https://download.youkuaiyun.com/download/richieandndsc/89876567