Java Web Reinforcement 07 JSTL core

Java Web Reinforcement 07 JSTL core

JSTL是什么, 是一个自定义的标记库, 像之前JSP中的<%开头的各种标记, <jsp开头的各种动作, EL表达式, 都是JSP规范中的规定. 然而这些还远远不够, 人们就自定义开发了一些方便快捷的标记库, 逐渐形成了一套第三方库的规范, JSTL就是其中之一. 所以JSTL并不是JSP规范的一部

JSTL是什么, 是一个自定义的标记库, 像之前JSP中的<%开头的各种标记, <jsp开头的各种动作, EL表达式, 都是JSP规范中的规定. 然而这些还远远不够, 人们就自定义开发了一些方便快捷的标记库, 逐渐形成了一套第三方库的规范, JSTL就是其中之一. 所以JSTL并不是JSP规范的一部分, 只是因为用的太普遍, 现在已经成为JSP开发的必备工具了. 要使用JSTL, 首先就需要从容器提供商或者其他地方获取jstl.jar包.
  1. JSTL的安装
  2. JSTL core 库
  3. 一般用途标签
  4. 条件标签
  5. 迭代标签
  6. URL标签
  7. 错误页面

JSTL的安装

JSTL是一种自定义标签, 更精确的说是自定义动作元素, 在之前学习中知道了EL表达式, <%@指令, <%scriplet, <%=表达式, JSP动作元素这五大JSP的特点. 所谓自定义标签, 自定义的就是JSP动作元素这一个部分; EL表达式, <%@指令, <%scriplet, <%=表达式就是JSP原始的功能, 也无法更改. Apache提供了JSTL的实现, 可以到Apache官方下载最新的JSTL包, 解压之后将其中的lib/下边的jar文件复制到WEB-INF/lib/下边, 这是Tomcat寻找库的第一个路径. 之后要配置一下tld文件, 也就是自定义标签文件的路径:
<jsp-config>
    <taglib>
    <taglib-uri>http://java.sun.com/jsp/jstl/fmt</taglib-uri>
    <taglib-location>/WEB-INF/fmt.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>http://java.sun.com/jsp/jstl/fmt-rt</taglib-uri>
    <taglib-location>/WEB-INF/fmt-rt.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>http://java.sun.com/jsp/jstl/core</taglib-uri>
    <taglib-location>/WEB-INF/c.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>http://java.sun.com/jsp/jstl/core-rt</taglib-uri>
    <taglib-location>/WEB-INF/c-rt.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>http://java.sun.com/jsp/jstl/sql</taglib-uri>
    <taglib-location>/WEB-INF/sql.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>http://java.sun.com/jsp/jstl/sql-rt</taglib-uri>
    <taglib-location>/WEB-INF/sql-rt.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>http://java.sun.com/jsp/jstl/x</taglib-uri>
    <taglib-location>/WEB-INF/x.tld</taglib-location>
    </taglib>
    <taglib>
    <taglib-uri>http://java.sun.com/jsp/jstl/x-rt</taglib-uri>
    <taglib-location>/WEB-INF/x-rt.tld</taglib-location>
    </taglib>
</jsp-config>
这些文件虽然看上去根本不存在, 但其实存在于jar包中, 其实这些文件存放在standard.jar中, Tomcat在运行的时候可以寻找到它们. 之后启用JSTL, 就需要使用JSP的三个指令之一: taglib来指定自定义的库:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
这么指定, 指定了使用JSTL的Core库, 前缀是c, 表示这个标签以<c:开头, 可见这种标签就是动作元素标签的样式. JSTL有如下几个库:
  1. Core, 前缀c, 主要用于一般用途, 展示, 条件和迭代标签
  2. I18N, 前缀fmt 一看就知道是国际化的标签
  3. Sql, 前缀sql, 用于访问关系型数据库
  4. Xml, 前缀x, 用于对xml文档进行操作
  5. Functions, 前缀fn, 包含了一组通用的EL函数, 就像自定义函数那样使用, 在EL表达式中可以使用这些函数.
这里就先来看看常用的Core库.

JSTL core库

core库中的标签主要分为四类:
  • 一般用途标签
    1. <c:out>, 用于输出, 等于<%=表达式
    2. <c:set>, 设置变量值
    3. <c:remove>, 删除指定变量
    4. <c:catch>, 捕获异常并且把异常对象放在指定的变量中
  • 条件标签
    1. <c:if>, 用于判断
    2. <c:choose>, <c:when>, <c:otherwise>, 一起使用, 相当于if-else
  • 迭代标签
    1. <c:forEach>, 遍历集合中的对象, 重复执行标签主体
    2. <c:forTokens>, 遍历字符串中用特定分隔符分割后的每个部分, 重复执行标签主体
  • URL标签
    1. <c:import>, 包含其他WEB资源, 与 <jsp:include 有些类似
    2. <c:url>, 按照特定规则重新构造URL
    3. <c:redirect>, 重定向
这些标签有很多, 在继续学习之前, 先要知道XML标签可以自闭合, 也可以包含其他内容, 如果一个XML标签包含其他内容, 则称其中的内容为这个标签的主体. 还记得之前要让大家将EL表达式看成一个对象, 在jsp动作元素中(包括JSTL), 凡是需要一个对象的地方(包括if判断时候的true和false也是对象), 都不能传入字符串, 而是要传入一个EL表达式才行. 为了使用JSTL表达式, 设置了一个控制器给请求附加了一些属性:

一般用途标签

在开始JSTL之前, 要知道JSTL里所有可以用表达式, 通常就是value属性对应的地方, 可以使用EL表达式, 也可以使用JSP原生的表达式, 也可以根据其他属性的值写字面常量, 会被解析成字符串. 而var属性对应的地方, 就直接写字符, 会被当成变量名来解析.

<c:out>标签

这个标签是自闭合标签. 用法是 <c:out value="表达式" default="表达式">标签. 其中默认值也可以写在主体内, <c:out value="表达式" default="表达式"><c:out 例如:
<c:out value="${person.owl.color}" default="NOCOLOR"/>
<c:out value="${owls[3].color}">${pageContext.request.method}</c:out>

<c:set>标签

这个标签用来为指定的变量设置值, 设置的目标可以是字符串类型的变量, JavaBean 或者Map类型的变量. 如果字符串类型的变量不存在, 则相当于定义一个变量. 设置 JavaBean和Map类型变量的时候, 则必须要求存在该变量. 在设置字符串变量的时候, 用法是: <:c:set var="变量名称" value="表达式"/>
//即使没有定义过叫做setBySet的变量, 也可以定义并获得
<c:set var="setBySet" value="gugugug"  scope="request"/>
<p>Set设置的字符串变量值是 ${setBySet}</p>
可以将value对应的属性写在主体内, 如 <c:set var="setBySet" scope="request">${person.owl}</c:set> 如果要给一个JavaBean设置某一个属性的值, 不能使用var属性, 而要使用target属性:
<c:set target="${person}" property="name" value="saner"/>
target属性接受一个表达式, 既然是对象, 就可以通过EL表达式获取, property是JavaBean的域名称, 需要与JavaBean一致, value依然可以接受表达式, 这里是saner字符串, 也可以接受其他的内容. 不光是字符串, 用对象赋值也可以, 只要符合JavaBean的定义:
//创建一个Owl类型的Bean
<jsp:useBean id="owl5" scope="request" class="com.example.domain.Owl"/>
<jsp:setProperty name="owl5" property="name" value="chocolate"/>
//把person变量的owl属性设置成owl5这个对象
<c:set target="${person}" property="owl" value="${owl5}"/>
//可以正常获取
<c:out value="${person.owl}"/>
对于给Map对象赋值, 也是类似的, target指向Map对象, property为键, value为值, 要符合Map对象中的类型.

<c:remove>标签

这个标签的作用就是removeAttribute, 在一个指定的作用域上删除变量, 如果不指定scope, 会删除所有作用域的对应的该变量名称.
<c:remove var="person" scope="request"/>
<p>${person}</p>
删除了person之后, 再尝试获取, 就获取不到了, 页面内用jsp:useBean设置的变量也可以删除. 这个标签没有带有主体的用法.

<c:catch>标签

这个标签用于捕获这个标签的主体中的异常, 将异常对象保存为一个变量, 用法是 <c:catch var="变量名称">, 所以这个标签必须要带有主体, 比如尝试一下:
<c:catch var="expcetion">
    <% int c = 1 / 0; %>
</c:catch>
<p><c:out value="${expcetion}"/> </p>
这个例子可以在页面中打印出: java.lang.ArithmeticException: / by zero.

条件标签

<c:if>标签

这个标签顾名思义, 就是一个判断标签, 可以带有主体也可以不带有主体. 不带有主体的情况下这么使用:
<c:if test="表达式" var="测试的结果存放的变量名称" scope="作用域"></c:if>
看到var就可以知道其中是字面常量, 而test其中是一个true或者false对象, 可以用el表达式或者Java代码. 会将表达式测试的结果放在变量名称中. 也就是说这个标签同时可以新建一个变量.
//设置一个字符串变量, 然后与属性进行比较
<c:set var="testname" value="saner"/>
<c:if test="${person.name==testname}" var="result1" scope="request"/>
<c:if test="${person.name!=testname}" var="result2" scope="request"/>
<c:if test="${person.name=='saner'}" var="result3" scope="request"/>
<p>${result1}</p>
<p>${result2}</p>
<p>${result3}</p>
这里要注意, 与字符串常量比较还可以使用单引号. 还可以有主体, 仅当判断是true的时候,主体的部分才会执行, 此时不使用变量名称.
<c:if test="${person.name==testname}">
    <%= "person.name与testname相等" %>
</c:if>
能够使用判断, 页面就变得更加的动态起来, 不仅展示的数据变了, 甚至页面的结构也会发生变化. 主体中的内容还可以是其他JSTL标签或者Java代码.

<c:choose>, <c:when>, <c:otherwise>

这三个必须连在一起用, choose标签在最外层, when对应一种判断, 判断通过就执行其中的主体, 然后结束, otherwise是如果其他判断都失败, 就执行这个, 这三个标签起到的作用相当于 if - else if - else的作用.
<c:choose>
    <c:when test="${postin==1}">
        <c:out value="1"/>
    </c:when>

    <c:when test="${postin==2}">
        <c:out value="2"/>
    </c:when>

    <c:otherwise>
        <c:out value="不是1也不是2"/>
    </c:otherwise>
</c:choose>
otherwise标签也可以不存在, 如果上边条件都没通过, 什么也不会做. 但如果出现otherwise标签, 只能有一个而且要放在最后.

迭代标签

迭代标签是展示数据最常使用的标签之一了, 可以将一个集合用一个临时变量来表示, 然后拆出其中所有数据.

<c:forEach>

这个标签能够自动遍历集合内容, 然后执行主体的代码. 用法是:
<c:forEach var="临时变量名称" items="集合对象" varStatus="LoopTagStatus" begin="1" end="10" step="2">
    主体
</c:forEach>
var就是临时字面变量名称, items指向的是集合对象, 所以可以用EL表达式或者Java的 <%= $> 表达式. begin, end, step表示起始和结束的索引以及步长, 如果有这三个属性的时候, 不指定items, 则临时变量就是数字, 用来生成顺序数字非常方便. 临时变量名称仅在反复执行的主体部分才有效. varStatus="LoopTagStatus", 这中间的LoopTagStatus可以自己名称, 这个名称对应着一个javax.servlet.jsp.jstl.core.LoopTagStatus对象, 从名字也能看出来, 是一个跟踪迭代的对象, 其内部有四个属性:
  1. count: 当前元素的序号, 从1开始
  2. index: 当前元素的序号, 从0开始
  3. first: 布尔值, 当前元素是否是第一个
  4. last: 布尔值, 当前元素是否是最后一个
例如, 取出owls中每一个对象, 然后如果是第一个就以红色显示颜色名称:
<c:forEach var="eachOwl" items="${owls}" varStatus="control">

    <p>第${control.count}个Owl的名字是${eachOwl.name},颜色是

        <c:if test="${control.first}">

            <span style="color:red">

        </c:if>

        ${eachOwl.color}

        <c:if test="${control.first}">

            </span>

        </c:if></p>

</c:forEach>
此标签能迭代的对象包括Set, List, Map, Iterator, Enumeration的实现类, 数组和以逗号分割的字符串. 以逗号分割的字符串指的就是常量:
<c:forEach var="temp" items="Cony, Jenny, Minko">
    <p>${temp}</p>
</c:forEach>
被逗号分开的每一个字符串的两侧空白会被省略(相当于trim()操作).

<c:forTokens>

这个标签是字符串特化的标签, 专门用于迭代指定的符号分割的字符串, 用法如下:
<c:forTokens items="fdk|dkaj|dfio|boiuv|fdsoui|fd" delims="|" var="temp" varStatus="control">
    ${temp}<c:if test="${control.last}"> 分割结束</c:if>
</c:forTokens>
除了items是字符串之外, 其他和forEach都很相似.

URL标签

这些标签应该说不是和URL相关, 而是和处理外部资源相关.

<c:import>

这个用来包含资源, 现在已经有了三种包含资源的方法:
  1. <%@ include file="" %>, 用来静态包含, 将被包含的内容放到当前jsp中一起编译
  2. <jsp:include page="***.jsp" %>, 用来动态包含这个JSP执行的结果
  3. <c:import url="http://...." var="name">, 与前两者不同, 可以包含当前Web容器之外的内容. 如果带有var, 会把包含的内容放到var的变量中, 而不是直接出现在当前位置.
<c:import url="http://...." >包含当前容器的内容时, 用法和其他的类似,也分为相对路径和绝对路径. 包含柚子小站的内容:
<p>包含柚子小站的内容</p>
<c:import url="https://conyli.cc" var="cony"/>
${cony}

<c:url>与<c:param>

这个标签用于URL操作, 可以生成URL. 还记得在客户端不支持Cookie的时候, 需要调用 resp.encodeURL() 来重写URL吗, 现在使用这个标签也可以做到. 这个标签的基础功能是生成URL, 使用方法如下
<c:url value="/gugu/saner" var="myurl" />
value中是一段URL, 可以是相对路径也可以是绝对路径, 如果是相对路径, 则就是相对路径, 如果是绝对路径, 前边会拼接上根路径. 然后把路径保存在var指定的变量名称内供取用. 还可以搭配<c:param>一起使用, 这个标签用于设定请求参数, 当<c:param>写在<c:url>内部的时候, 会将其进行合理的编码然后拼接成一段URL, 例如:
<c:url value="/jstl/normal" var="myurl">
    <c:param name="name" value="saner"/>
    <c:param name="que" value="<>KF"/>
</c:url>
<a href="${myurl}">链接</a>
生成的链接是: /gugu/saner?name=saner&que=%3c%3eKF, 发现没有, 只要使用c:param搭配使用, 就可以实现Session中的重写URL的功能.

<c:redirect>

这个标签用于把请求重定向到其他Web资源, 用法是 <c:redirect url="http://..."> 一旦出现这个标签, 也就意味着是一个301响应. 其他的操作都无法执行了. 也可以重定向到当前容器的相对路径和绝对路径. 还有一个context属性, 以后来补齐.

错误页面

既然前边看到了<c:catch, 就顺便学习一下错误页面. 如果不进行任何的错误处理, 服务器会直接把错误暴露出来. 可以设置一个错误页面, 有两种方式:
  1. <%@ page isErrorPage="true" %>, 将当前页面设置成一个错误页面, 和<%@ page errorPage="error.jsp" %>搭配使用, 出错的话就会去找那个页面.
  2. 在web.xml中配置, 这个优先级不如第一条. 但是很灵活, 可以为不同的响应码设置不同的错误页面. 例如:
        <error-page>
            <location>/error.jsp</location>
            <error-code>404</error-code>
        </error-page>
    
    这会在出现404错误的时候显示error.jsp页面. 错误页面的好处是不会重新显示URL, 用户看到的依然是那个错误的URL. 还可以针对具体的错误类型配置错误页面.
回头来看错误页面, 如果一个页面被指定为<%@ page isErrorPage="true" %>, 则在其中可以捕获一个 ${pageContext.exception}, 是捕获的错误对象. 错误页面与 <c:catch 的区别是, 一个使用另外一个页面来处理错误, 一个让jsp自己可以从错误中恢复.
LICENSED UNDER CC BY-NC-SA 4.0
Comment