JSTL是Sun公司开发的一套用于JSP页面中的标签库,使用JSTL可以在页面中实现一些简单的逻辑,从而替换页面中的Java代码。JSTL通常也会配合EL表达式一起使用,Sun公司的JSTL标签库封装了大量的方法,当然如果JSTL中的标签不能满足我们的需求时,我们还可以通过使用自定义标签达到我们的需求,关于自定义标签会在之后的文章中讲解。
JSTL标签库是一个很大的库,主要包含了以下几种:
⑴ JSTL核心标签库——c.tld
⑵ 国际化标签——fmt.tld
⑶ JSTL函数(EL函数)——fn.tld
⑷ 数据库标签 (不符合MVC模式,不用学)——sql.tld
⑸ XML标签 (不符合MVC模式,不用学)——x.tld
JSTL函数已经在《EL表达式(3)》中学习过,这里就不再叙述;国际化标签会在之后的文章中讲解和学习。今天主要来学习JSTL核心标签库。
在MyEclipse10中,创建JavaEE 5.0以上的版本都会默认带上JSTL开发包,并且使用的是JSTL1.2版本:
而如果创建的是J2EE 1.4及以下的版本,需要自己选择是否添加JSTL支持,并且如果选择添加,那么导入的是JSTL1.1版本:
JSTL1.2版本和JSTL1.1的版本在JAR包上的区别是JSTL1.2只需要“jstl-1.2.jar”一个包就足够了,而JSTL1.1需要“jstl.jar”包和“standard.jar”包的支持。如果我们在工程中缺少相对应的包,只需要选择合适的jar包导入即可。
当然,这只是外部版本的区别,其实不管是JSTL1.2版本还是JSTL1.1的版本,对于JSTL核心标签库都是使用“JSTL 1.1 core library”,我想区别可能是另外几个库吧。
在本篇中,我们均是以Java EE 5.0为WEB的Java版本,因此均采用JSTL1.2版本。
除了要导入需要的jar包,要想使用JSTL标签,还必须在JSP页面中使用taglib指令导入需要的标签库。
语法:
<%@ taglib uri="。。。" prefix="。。。" %>
uri属性:用来指定使用JSTL标签库中的哪个标签库。
prefix属性:用来指定在JSP页面中使用该标签的前缀文字。
那么如何使用uri属性来指定使用哪个标签库呢?我们可以点击工程中的“jstl-1.2.jar”包:
这个jar包中有很多包和类,在最下面有一个“META-INF”目录,这里面保存了很多标签所需要的tld文件,分别代表不同的库,而我们所需要的jstl核心库就是“c.tld”文件:
对于jstl核心库的所有标签,都在这个“c.tld”文件中进行了声明描述。而这些所有的标签都绑定到了一个uri上,通过在JSP中使用taglib指令导入这个uri,那么我们就能使用这个标签库了,而这个uri就在“c.tld”文件的<taglib>标签下:
因此我们在JSP页面中使用taglib指令就可以这样导入该标签库:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
这里taglib指令中的uri一定要和tld文件中的uri一致,而prefix前缀通常采用tld文件的文件名,这样容易明白这个标签出自哪个JSTL库。
通过以上步骤,JSTL标签库的环境就已经配置好了,接下来我们就可以使用JSTL的核心库标签了。
补充前言:标签中如果含有“var”属性,就是讲该标签的执行结果以“var”属性的值存储到某个域中,通常都会有“scope”属性来指定存储到哪个域中,如果没有一般都是存储到page域中。
下面介绍几个JSTL核心标签库中的几个标签的使用,较为常用的有前面三个:
使用<c:forEach>标签可以配合EL表达式对使用EL获取的集合进行迭代,这一点光凭EL表达式是无法做到的。
例1-1:
1 <% 2 ArrayList<Person> list = new ArrayList<Person>(); 3 list.add(new Person("Ding",25)); 4 list.add(new Person("LRR",24)); 5 pageContext.setAttribute("personList", list); 6 %> 7 8 <c:forEach var="person" items="${personList}"> 9 ${person.name } <br> 10 </c:forEach>
分析:这个例子中在<c:forEach>标签内,“items”属性用于指定获取迭代的对象。记住,这里一定要用EL表达式才能获取存储在域中的对象。而“var”属性则是对每次迭代的元素以var的值为关键字存在域中,就好像使用了Map集合一样,对集合中的每个元素作为value值,而“var”名称作为key一样。
在上面的示例中,items通过EL表达式获取域对象中的“list”集合,而对“list”集合的每次迭代都是一个Person对象,这个Person对象使用了var属性指定的值作为引用。因此在标签体中,再次使用了EL表达式,而这个EL表达式中的“person”既是var属性所指的名称,也是每次迭代的Person对象的引用,而Person对象中含有name属性,因此可以使用EL表达式取出,这点与之前学过的EL获取数据还是一样的道理。在浏览器中观察:
例1-2:
1 <% 2 Map<String,Person> map = new HashMap<String,Person>(); 3 map.put("p1",new Person("Ding",25)); 4 map.put("p2",new Person("LRR",24)); 5 pageContext.setAttribute("personMap", map); 6 %> 7 8 <c:forEach var="entry" items="${personMap}"> 9 ${entry.value.name } <br> 10 </c:forEach>
分析:在这个例子中,“items”属性依然还是获取要迭代的对象(使用EL表达式才能获取域对象中保存的要迭代的集合对象)。而这里我们使用<c:forEach>标签并不是真正对Map集合进行迭代,而是将Map集合先调用了entrySet方法,获取到的是Set<Map.Entry<K,V>>集合,然后再对这个Set集合进行迭代:
那么也就是说对于“var”属性,指定的都是对Set集合进行迭代出来元素的引用,而Set集合迭代出来的每个元素都是Map.Entry集合,即以“var”名称所引用。因此上述示例中的var属性的值“entry”代表了每次迭代出来的Map.Entry集合,而这个Map.Entry集合中含有key属性和value属性(通过API可以看到含有getKey方法和getValue方法),因此在标签体中,${entry.value}代表了存在Map集合中的Person对象,另外${entry.key}代表了Map集合中Person对象所被引用的key值(如”p1”)。
在浏览器中观察:
通过上面两个例子,可以大体了解了<c:forEach>标签的使用,当然<c:forEach>标签还有一些需要了解的地方:
例1-3:
1 <c:forEach var="num" begin="1" end="9" step="1" > 2 ${num } 3 </c:forEach>
浏览器观察:
可以看到在<c:forEach>标签中,如果没有“items”属性,那么就从begin属性指定的值开始迭代,直到end属性指定的值为止,而“var”属性依然是作为每次迭代出来元素的引用。
在<c:forEach>标签中还有一个“varStatus”属性,这个属性用来封装保存迭代信息的对象,比如这是第几个迭代对象,是否是第一个迭代对象,是否是最后一次迭代对象等等,在“varStatus”属性下,还有属于varStatus的几个从属性,这些可以配合EL表达式使用:
index:当前这次迭代从 0 开始的迭代索引
count:当前这次迭代从 1 开始的迭代计数
first:用来表明当前这轮迭代是否为第一次迭代的标志
last:用来表明当前这轮迭代是否为最后一次迭代的标志
begin:属性值
end:属性值
step:属性值
例1-4:
1 <% 2 ArrayList<Person> list = new ArrayList<Person>(); 3 list.add(new Person("Ding",25)); 4 list.add(new Person("LRR",24)); 5 6 pageContext.setAttribute("personList", list); 7 %> 8 9 <c:forEach var="person" items="${personList }" varStatus="listInfo" > 10 ${person.name } ---- ${listInfo.count } <br> 11 </c:forEach>
浏览器中观察:
这里使用到的是”varStatus”属性的从属性“count”,用来表示当前这次迭代从 1 开始的迭代计数。其他的从属性都是类似的用法。
<c:if test=“。。。”>标签(test属性是必须的)用于测试某个条件是否成立,经常配合EL表达式使用。
例2-1:
1 <c:if test="${user!=null}"> 2 欢迎您:user.name 3 </c:if>
分析:“test”属性只接收布尔类型,当“test”属性为“true”时才执行<c:if>标签的标签体中的内容,当“test”属性为“false”时标签体内容自动忽略。
例2-2:
1 <c:if var="booleanTest" test="${user!=null}" scope="page"> 2 欢迎您:user.name 3 </c:if> 4 5 ${booleanTest }
输出:false
分析:“test”属性返回Boolean性结果(true或false),“var”属性均是作为存储数据用的,这里是将“test”属性的结果以“var”属性的值(这里是booleanTest)存储到某个域中,存到什么域呢,由“scope”属性指定。这样最后能由EL表达式从域中根据“var”属性的值取出相对应的数据。
总结:只要某个标签含有“var”属性,就是讲该标签的执行结果以“var”属性的值存储到某个域中,通常都会有“scope”属性来指定存储到哪个域中,如果没有一般都是存储到page域中。
<c:url>标签用于在JSP页面中创建一个URL地址,最关键的目的在于<c:url>标签能对其“value”属性提供的URL地址进行重写,加入JSESSIONID,以便于在访问禁用cookie的主机能为其创建session等功能。这一点在《Servlet的学习之Session(3)》 中已经介绍过。
下图为<c:url>标签中的属性介绍:
例3-1:
1 <c:url var="index" value="${pageContext.request.contextPath}/index" /> 2 3 <a href="${index}" >首页</a>
浏览器观察:
查看网页源码:
可以看到在首次访问时确实在URL后面跟了JSESSIONID,这是<c:url>标签用于防止客户浏览器禁止了Cookie,同样的,如果服务器发现了客户机浏览器并没有禁用cookie的话,那么再次访问的话,<c:url>标签会将这个JSESSIONID会自动删去,这一点同使用Response.encodeURL()方法或Response.encodeRedirectURL()方法是一样的。
另外:<c:url>标签中使用了“var”属性,就是将“value”中的URL进行重写后保存在域中(默认为page域,可用“scope”选择域)。那么在其他HTML标签中如果有需要URL的超链接时,只需要使用EL表达式从域中取出即可,取出的即使经过URL重写的超链接URL,正如上面的例子。
例3-2:
<c:url value="/index" />
在浏览器中观察:
如果没有使用到“var”属性,那么这个标签会将内容直接显示在网页中,这一点要注意。同时该注意的还有这种没有“var”属性,会自动在URL前面添加web应用的名称!所以在没有使用“var”属性的<c:url>标签中的“value”属性一定不要添加web应用的名称。
例3-3:
<a href='<c:url value="/index" />'>首页</a>
在浏览器中观察及网页源码:
通常<c:url>标签还配合<c:param>标签一起使用。使用<c:param>标签可以在URL后面添加请求参数,更为重要的一点在于,使用<c:param>标签能对参数中的中文进行URL重写,这一点在《Servlet的学习之Request请求对象(2)》中说明了另一种解决方法。
例3-4:
1 <c:url var="index" value="${pageContext.request.contextPath}/index.jsp" > 2 <c:param name="username" value="银魂"></c:param> 3 </c:url> 4 5 <a href="${index}">首页</a>
在浏览器中观察及其网页源码:
可以看到在网页源码中,URL地址后面已经跟随了请求参数,同时当请求参数中包含中文时也对中文进行了URL重写。
例2-1:
<c:out value="aaaa" />
输出:aaaa。
例2-2:
<c:out value="<a href='www.baidu.com'>百度</a>" escapeXml="true" />
输出:
<c:set>标签用于把一个对象或数据存在指定的域范围内,或者设置web域中的java.util.Map类型的属性对象,或者JavaBean类的属性对象的属性。
例4-1:
1 <c:set var="name" value="ding" scope="page" /> 2 3 ${name }
输出:ding。
例4-2:
1 <% 2 HashMap map = new HashMap(); 3 pageContext.setAttribute("map", map); 4 %> 5 6 <c:set property="name" value="ding" target="${map}" ></c:set> 7 8 ${map.name }
输出:ding。
例4-3:
1 <% 2 Person p = new Person(); 3 pageContext.setAttribute("person", p); 4 %> 5 6 <c:set property="age" value="25" target="${person}" ></c:set> 7 8 ${person.age }
输出:25
<c:remove>标签用于移除web域中的各种保存的数据。
语法格式:
<c:remove var=”保存的数据引用名” [scope=“{ page(默认) | request | session | application }”] />
<c:redirect>标签用于进行网页的重定向。