EL和JSTL专题

EL简介

EL全名为Expression Language,它原本是JSTL 1.0为方便存取数据所自定义的语言。当时只能在JSTL标签中使用,如下:

<c:out value="${3+7}">

程序执行结果为10。但是你却不能直接在JSP网页中使用:

<p>Hi !${username}</p>

到了JSP 2.0 之后,EL已经正式纳入称为标准规范之一。因此,只要是支持Servlet 2.4/JSP 2.0的 Container,就都可以在JSP网页中直接使用EL了。

除了JSP2.0 建议使用EL之外,JavaServer Faces(JSR-127)也考虑将EL纳入规范,由此可知,EL如今已经是一项成熟、标准的技术。

注意:

假若您所用的Container只支持Servlet 2.3/JSP 1.2,如:Tomcat 4.1.29,您就不能在JSP网页中直接使用EL,必须安装支持Servlet 2.4/JSP 2.0 的Container。

EL语法

EL语法很简单,它最大的特点就是使用上很方便。

${sessionScope.user.sex}

  所有EL都是以${为起始、以}为结尾的。上述范例的意思是:从Session的范围中,取得用户的性别。假若依照之前JSP Scriptlet 的写法如下:

User user =(User)session.getAttribute("user");

String sex = user.getSex();

两者相比较之下,可以发现EL的语法比传统JSP Scriptlet更为方便、简洁。

与[]运算符

  EL提供.和[]两种运算符来存取数据。

下列两者所代表的意思是一样的:

${sessionScope.user.sex}

等于

${sessionScope.user["sex"]}

.和[]也可以同时混合使用,如下:

${sessionScope.shoppingCart[0].price}

回传结果为shoppingCart中第一项物品的价格。

不过,一下两种情况,两者会有差异:

(1)当要存取的属性名称中包含一些特殊字符,如.或-等并非字母或数字的符号,就一定要使用[],例如:

${user.My-name }

上述是不正确的方式,应当改为:

${user["My-name"]}

(2)我们来考虑下列情况:

${sessionScope.user[data]}

此时,data是一个变量,假若data的值为“sex”,那上述的例子等于${sessionScope.user.sex};假若data的值为“name”时,它就等于${sessionScope.user.sex}。因此,如果要动态取值时,就 可以用上述的方法来做,但,无法做到动态取值。

接下来,我们更详细地来讨论一些情况,首先假设有一个EL:

${expr-a[expr-b]}

(1)当expr-a的值为null时,它会回传null。

(2)当expr-b的值为null时,它会回传null。

(3)当expr-a的值为Map类型时:

·假若!value-a.containsKey(value-b)为真,则回传null。

·否则回传value-a.get(value-b)。

(4)当expr-a的值为List或array类型时:

·将value-b的值强制转型为int,假若不能转型int时,会产生error。

·然后,假若value-a.get(value-b)或Array.get(value-a, value-b)产生ArrayIndexOutOfBoundsException或IndexOutOfBoundsException时,则回传null。

·假若value-a.get(value-b)或Array.get(value-a,value-b)产生其他的异常时,则会产生error。

·最后都没有任何异常产生时,回传value-a.get(value-b)或Array.get(value-a,value-b)。

(5)当expr-a的值为JavaBean对象时:

·将value-b的值强制转型为String。

·假若getter产生异常时,则会产生error。若没有异常产生时,则回传getter的结果。

EL变量

EL存取变量数据的方法很简单,例如:${username}。他的意思是去除某一范围中名称为username的变量。因为我们并没有指定哪一个范围的username,所以他的默认值会先从Page范围找,加入找不到,再依次序到Request、Session、Application范围。假若途中找到username,就直接回传,不再继续找下去,但是加入全部的范围都没有找到时,就回传null

四大存储区

其中,pageScope、requestScope、sessionScope和applicationScope都是EL的隐含对象,由它们的名称可以很容易猜出它们所代表的意思,例如:${sessionScope.username}是取出Session范围的username变量。这种写法是不是比之前JSP的写法:

String username =(String)session.getAttribute("username");

容易、简洁许多。有关EL隐含对象一会讲解。

自动转变类型

EL除了提供方便存取变量的语法,它另外一个方便的功能就是:自动转变类型,我们来看下面这个范例:

${param.count+20}

假若窗体传来的count的值为10时,那么上面的结果为30.之前没接触过JSP的读者可能会认为上面的例子是理所当然的,但是在JSP 1.2之中不能这样做,原因是从窗体所传来的值,它们的类型一律是String,所以当你接受之后,必须再讲它转为其他类型,如:int、float等等,然后才能执行一些数学运算,下面是之前的做法:

String str_count = request.getParameter("count");

int count = Integer.parseInt(str_count);

count = count+20;

接下来再详细说明EL类型转换的规则:

(1)将A转为String类型

  ·假若A为String时:回传A

  ·否则,当A为null时:回传“”

  ·否则,当A.toString()产生异常时:错误!

  ·否则,回传A.toString()

(2)将A转为Number类型的N

  ·假若 A 为 null 或 "" 时:回传 0

  ·假若 A 为 Character 时:将 A 转为 new Short((short)a.charValue( ))

  ·假若 A 为 Boolean 时:错误!

  ·假若 A 为 Number 类型和 N 一样时:回传 A

  ·假若 A 为 Number 时:

    ·假若 N 是 BigInteger 时:

      ·假若 A 为 BigDecimal 时:回传 A.toBigInteger( )

      ·否则,回传 BigInteger.valueOf(A.longValue( ))

    ·假若 N 是 BigDecimal 时:

      ·假若 A 为 BigInteger 时:回传 A.toBigDecimal( )

      ·否则,回传 BigDecimal.valueOf(A.doubleValue( ))

    ·假若 N 为 Byte 时:回传 new Byte(A.byteValue( ))

    ·假若 N 为 Short 时:回传 new Short(A.shortValue( ))

    ·假若 N 为 Integer 时:回传 new Integer(A.intValue( ))

    ·假若 N 为 Long 时:回传 new Long(A.longValue( ))

    ·假若 N 为 Float 时:回传 new Float(A.floatValue( ))

    ·假若 N 为 Double 时:回传 new Double(A.doubleValue( ))

    ·否则,错误!

  ·假若A为String时:

    ·假若 N 是 BigDecimal 时:

      ·假若 new BigDecimal(A)产生异常时:错误!

      ·否则,回传 new BigDecimal(A)

    ·假若 N 是 BigInteger 时:

      ·假若 new BigInteger(A)产生异常时:错误!

      ·否则,回传 new BigInteger(A)

    ·假若 N.valueOf(A)产生异常时:错误!

    ·否则,回传 N.valueOf(A)

  ·否则,错误!

(3) 将 A 转为 Character 类型
  ● 假若 A 为 null 或 "" 时:回传 (char)0
  ● 假若 A 为 Character 时:回传 A
  ● 假若 A 为 Boolean 时:错误!
  ● 假若 A 为 Number 时:转换为 Short 后,然后回传 Character
  ● 假若 A 为 String 时:回传 A.charAt(0)
  ● 否则,错误!

(4) 将 A 转为 Boolean 类型

  ● 假若 A 为 null 或 "" 时:回传 false

    ● 否则,假若 A 为 Boolean 时:回传 A 

  ● 否则,假若 A 为 String,且 Boolean.valueOf(A)没有产生异常时:回传 Boolean.valueOf(A)

  ● 否则,错误!

EL保留字

EL保留字表

所谓保留字的意思是指变量在命名时,应该避开上述的名字,以免程序编译时发生错误。

EL隐含对象

这部分 很重要

1.与范围有关的隐含对象

  applicationScope

  sessionScope

  requestScope

  pageScope

2.与输入有关的隐含对象

  param

  paramValue

3.其他隐含对象

  cookie

  header

  headerValues

  initParam

  PageContext

与范围有关的EL隐含对象包含以下四个:pageScope、requestScope、sessionScope和applicationScope,它们基本上就和JSP的PageContext、request、session和application一样,所以笔者在这里只稍略说明。不过必须注意的是,这四个隐含对象只能用来取得范围属性值,即JSP中的getAttribute(String name),却不能取得其他相关信息,例如:JSP中的request对象除可以存取属性之外,还可以取得用户的请求参数或表头信息等等。例如:JSP中的request对象除可以存取属性之外,还可以取得用户的请求参数或表头信息等等。但是在EL中,它就只能单纯用来取得对应范围的属性值,例如:我们要在session中存储一个属性,它的名称为username,在JSP中使用session.getAttribute("username")来取得其值的。接下来分别对着四个隐含对象做简短的说明:

  ·pageScope

  范围和JSP的Page相同,也就是丹丹一页JSP Page的范围(Scope)

  ·requestScope

  范围和JSP的Request相同,requestScope的范围是从一个JSP网页请求到另一个JSP网页请求之间,随后此属性就会失效。

  · sessionScope

  范围和JSP Scope的session相同,它的属性范围就是用户持续在服务器连接的时间

  ·applicationScope

  范围和JSP Scope中的application相同,它的属性范围是从服务器一开始执行服务,到服务器关闭为止。

与输入有关的隐含对象

  与输入有关的隐含对象由两个:param和paramValues,它们是EL中比较特别的隐含对象。一般而言,我们在取得用户的请求参数时,可以利用下列方法:

  request.getParameter(String name)

  requext.getParameterValues(String name)

在EL中则可以 使用param和paramValues两者来取得数据、

${param.name}

${paramValues.name}

这里param的功能和request.getParameter(String name)相同,而paramValues和request.getParameterValues(String name)相同。如果用户填了一个表格,表格名称为username,则我们就可以使用${param.username}来取得用户填入的值。

  为了让读者更加了解param和paramValues隐含对象的使用,再来看下面这个范例。

这是html:

 1 <html>
 2 <head>
 3 <title>CH6 - Param.html</title>
 4 </head>
 5 <body>
 6 <h2>EL 隐含对象 param、paramValues</h2>
 7 <form method = "post" action = "Param.jsp">
 8 <p>姓名:<input type="text" name="username" size="15" /></p>
 9 <p>密码:<input type="password" name="password" size="15" /></p>
10 <p>性别:<input type="radio" name="sex" value="Male" checked/>11 <input type="radio" name="sex" value="Female" /></p>
12 <p>年龄:
13 <select name="old">
14 <option value="10">10 - 20</option>
15 <option value="20" selected>20 - 30</option>
16 <option value="30">30 - 40</option>
17 <option value="40">40 - 50</option>
18 </select>
19 </p>
20 <p>兴趣:
21 <input type="checkbox" name="habit" value="Reading"/>看书
22 <input type="checkbox" name="habit" value="Game"/>玩游戏
23 <input type="checkbox" name="habit" value="Travel"/>旅游
24 <input type="checkbox" name="habit" value="Music"/>听音乐
25 <input type="checkbox" name="habit" value="Tv"/>看电视
26 </p>
27 <p>
28 <input type="submit" value="传送"/>
29 <input type="reset" value="清除"/>
30 </p>
31 </form>
32 </body>
33 </html>

JSP代码:

 1 <%@ page contentType="text/html;charset=GB2312" %>
 2 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 3 <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
 4 <html>
 5 <head>
 6 <title>CH6 - Param.jsp</title>
 7 </head>
 8 <body>
 9 <h2>EL 隐含对象 param、paramValues</h2>
10 <fmt:requestEncoding value="GB2312" />
11 姓名: ${param.username}</br>
12 密码: ${param.password}</br>
13 性别: ${param.sex}</br>
14 年龄: ${param.old}</br>
15 兴趣: ${paramValues.habit[0]}
16 ${paramValues.habit[1]}
17 </body>
18 </html>


由Param.html窗体传过来的值,我们必须制定编码方式,才能够确保Param.jsp能够顺利接受中文,传统做法为:

1 <%
2 request.setCharacterEncoding("GB2312");
3 %>

假若是使用JSTL写法时,必须使用I18N格式处理的标签库,如下:

 1 <fmt:requestEncoding value="GB2312" /> 

Param.jsp主要使用EL的隐含对象param来接受数据。但是必须注意:假若要取得多重选择的复选框的值时,必须使用ParamValues,例如:使用paramValues来取得“兴趣”的值,不过这里我就只显示两个“兴趣”的值:

 1 ${param.username} 2 ……… 3 ${paramValues.habit[0]} 4 ${paramValues.habit[1]} 

这是Param.jsp的执行结果:

其他隐藏对象

  ·cookie

  所谓的cookie是一个小小的文本文件,它是以key、value的方式将SessionTracking的内容记录在这个文本文件内,这个文本文件通常存在于浏览器的暂存内。JSTL并没有提供设定cookie的动作,因为这个动作通常存在于浏览器的暂存区内。JSTL并没有提供设定cookie的动作,因为这个动作通常都是后端开发者必须去做的事情,而不是交给前端开发者。假若我们在cookie中设定一个名称为userCountry的值,那么可以使用${cookie.userCountry}来取得它。

  ·header和headerValues

  header存储用户浏览器和服务端用来沟通的数据,当用户要求服务端的网页时,会送出一个记载要求信息的标头文件,例如:用户浏览器的版本、用户计算机所设定的区域等其他相关数据。假若要取得用户浏览器的版本,即${header["User-Agent"]}。另外在鲜少机会下,有可能同一标头名称拥有不同的值,此时必须改为使用headerValues来取得这些值。

注意:

因为User-Agent中包含“-”这个特殊字符,所以必须使用“[]”,而不能写成$(header.User-Agent)

·initParam

就像其他属性一样,我们可以自行设定web站台的环境参数(Context),当我们想取得这些参数时,可以使用initParam隐含对象去取得它。例如:我们在web.xml中设定如下:

 1 <?xml version="1.0" encoding="ISO-8859-1"?>
 2 <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
 5 version="2.4">
 6  7 <context-param>
 8 <param-name>userid</param-name>
 9 <param-value>mike</param-value>
10 </context-param>
11 12 </web-app>

那么我们就可以直接使用${initParam.userid}来取得名称为userid,其值为mike的参数。下面是之前的做法:
String userid = (String)application.getInitParameter("userid");

·PageContext

我们可以使用${pageContext}来取得其他有关用户要求或页面的详细信息。

 

 1 <%@ page contentType="text/html;charset=GB2312" %>
 2 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 3 <html>
 4 <head>
 5 <title>CH6 - pageContext.jsp</title>
 6 </head>
 7 <body>
 8 <h2>EL 隐含对象 pageContext</h2>
 9 ${pageContext.request.queryString}:${pageContext.request.queryString}</br>
10 ${pageContext.request.requestURL}:${pageContext.request.requestURL}</br>
11 ${pageContext.request.contextPath}:${pageContext.request.contextPath}</br>
12 ${pageContext.request.method}:${pageContext.request.method}</br>
13 ${pageContext.request.protocol}:${pageContext.request.protocol}</br>
14 ${pageContext.request.remoteUser}:${pageContext.request.remoteUser}</br>
15 ${pageContext.request.remoteAddr }:${pageContext.request.remoteAddr}</br>
16 ${pageContext.session.new}:${pageContext.session.new}</br>
17 ${pageContext.session.id}:${pageContext.session.id}</br>
18 </body>
19 </html>

pageContext.jsp 的执行结果如图 6-3,执行时必须在 pageContext.jsp 之后加上?test=1234,即PageContext.jsp?test=1234,这样${pageContext.request.queryString}才会显示test=1234。

注意:因为${}在JSP 2.0 中是特殊字符,JSP容易会自动将它当做EL来执行,因此,假若要显示${}时,必须在$前加上,如:${XXX}

EL 算术运算符

EL算术运算符主要有以下五个:

(1) A {+ ,- , *} B

● 假若 A 和 B 为 null:回传 (Long)0
● 假若 A 或 B 为 BigDecimal 时,将另一个也转为 BigDecimal,则:
·假若运算符为 + 时:回传 A.add(B)
·假若运算符为 - 时:回传 A.subtract(B)
·假若运算符为 * 时:回传 A.multiply(B)
● 假若 A 或 B 为 Float、Double 或包含 e / E 的字符串时:
·假若 A 或 B 为 BigInteger 时,将另一个转为 BigDecimal,然后依照运算符执行
运算
·否则,将两者皆转为 Double,然后依照运算符执行运算
● 假若 A 或 B 为 BigInteger 时,将另一个也转为 BigInteger,则:
·假若运算符为 + 时:回传 A.add(B)
·假若运算符为 - 时:回传 A.subtract(B)
·假若运算符为 * 时:回传 A.multiply(B)
● 否则,将 A 和 B 皆转为 Long,然后依照运算符执行运算
● 假若运算结果产生异常时,则错误!

(2) A {/ , div} B
● 假若 A 和 B 为 null:回传 (Long)0
● 假若 A 或 B 为 BigDecimal 或 BigInteger 时,皆转为 BigDecimal,然后回传 A.divide(B,
BigDecimal.ROUND_HALF_UP)

● 否则,将 A 和 B 皆转为 Double,然后依照运算符执行运算
● 假若运算结果产生异常时,则错误!
(3) A {% , mod} B
● 假若 A 和 B 为 null:回传 (Long)0
● 假若 A 或 B 为 BigDecimal、Float、Double 或包含 e / E 的字符串时,皆转为 Double,然
后依照运算符执行运算
● 假若 A 或 B 为 BigInteger 时,将另一个转为 BigInteger,则回传 A.remainder(B)
● 否则,将 A 和 B 皆转为 Long,然后依照运算符执行运算
● 假若运算结果产生异常时,则错误!
(4) -A
● 假若 A 为 null:回传 (Long)0
● 假若 A 为 BigDecimal 或 BigInteger 时,回传 A.negate( )
● 假若 A 为 String 时:
·假若 A 包含 e / E 时,将转为 Double,然后依照运算符执行运算
·否则,转为 Long,然后依照运算符执行运算
·假若运算结果产生异常时,则错误!
● 假若 A 为 Byte、Short、Integer、Long、Float 或 Double
·直接依原本类型执行运算
·假若运算结果产生异常时,则 错误!
● 否则,错误!

EL关系运算符

注意:

在使用EL关系运算符时,不能够写成:

${param.password}==${param.password2}

或者

${${param.password1}==${param.password2}}

而应写成

${param.password1==param.password2}

 (1) A {<, >, <=, >=, lt, gt, le, ge} B
● 假若 A= = B,运算符为<=, le, >=, ge 时,回传 true,否则回传 false
● 假若 A 为 null 或 B 为 null 时,回传 false
● 假若A或B为BigDecimal时,将另一个转为BigDecimal,然后回传A.compareTo(B)的值
● 假若 A 或 B 为 Float、Double 时,皆转为 Double 类型,然后依其运算符运算
● 假若 A 或 B 为 BigInteger 时,将另一个转为 BigInteger,然后回传 A.compareTo(B)的值
● 假若 A 或 B 为 Byte、Short、Character、Integer 或 Long 时,皆转为 Long 类型,然后依其
运算符运算
● 假若 A 或 B 为 String 时,将另一个也转为 String,然后做词汇上的比较
● 假若 A 为 Comparable 时,则:
·假若 A.compareTo(B)产生异常时,则错误!
● 否则,采用 A.compareTo(B) 的比较结果
● 假若 B 为 Comparable 时,则:
· 假若 B.compareTo(A)产生异常时,则错误!
● 否则,采用 A.compareTo(B) 的比较结果
● 否则,错误!
(2) A {= =, !=, eq, ne} B
● 假若 A= = B,依其运算符运算
● 假若 A 为 null 或 B 为 null 时:= = /eq 则回传 false,!= / ne 则回传 true
● 假若 A 或 B 为 BigDecimal 时,将另一个转为 BigDecimal,则:
· 假若运算符为 = = / eq,则 回传 A.equals(B)
· 假若运算符为 != / ne,则 回传 !A.equals(B)

● 假若 A 或 B 为 Float、Double 时,皆转为 Double 类型,然后依其运算符运算
● 假若 A 或 B 为 BigInteger 时,将另一个转为 BigInteger,则:
·假若运算符为 = = / eq,则 回传 A.equals(B)
·假若运算符为 != / ne,则 回传 !A.equals(B)
● 假若 A 或 B 为 Byte、Short、Character、Integer 或 Long 时,皆转为 Long 类型,然后依其
运算符运算
● 假若 A 或 B 为 Boolean 时,将另一个也转为 Boolean,然后依其运算符运算
● 假若 A 或 B 为 String 时,将另一个也转为 String,然后做词汇上的比较
● 否则,假若 A.equals(B)产生异常时,则 错误!
● 否则,然后依其运算符运算,回传 A.equals(B)

EL逻辑运算符

例子:

${param.month==7 and param.day ==14}

${param.month == 7 || param.day ==14}

${not param.choice}

EL逻辑运算符的规则很简单:

(1) A {&&, and, || 或 or } B
·将 A 和 B 转为 Boolean,然后依其运算符运算
(2) {!, not}A
·将 A 转为 Boolean,然后依其运算符运算

EL 其他运算符

EL除了上述三大类的运算符之外,还有下列几个重要的运算符:

(1)empty运算符

(2)条件运算符

(3)()括号运算符

empty运算符

empty运算符主要用来判断值是否为null或空的,例如:

${empty param.name}

empty运算符的规则:

(1) {empty} A
● 假若 A 为 null 时,回传 true
● 否则,假若 A 为空 String 时,回传 true
● 否则,假若 A 为空 Array 时,回传 true
● 否则,假若 A 为空 Map 时,回传 true
● 否则,假若 A 为空 Collection 时,回传 true
● 否则,回传 false

EL条件运算符

如下:

${A?B:C}

意思是说,当A为true时,执行B;而A为false时,则执行C。

EL括号运算符

括号运算符主要用来改变执行优先权,例如:${A*(B+C)}

至于运算符的优先权,如下所示:

· []、.
· ( )
· - (负)、not、!、empty
· *、/、div、%、mod
· +、- (减)
· <、>、<=、>=、lt、gt、le、ge
· = =、!=、eq、ne
· &&、and

· ||、or
· ${ A ? B : C}

 1 <%@ page contentType="text/html;charset=GB2312" %>
 2 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 3 <html>
 4 <head>
 5 <title>CH6 – ELOperator.jsp</title>
 6 </head>
 7 <body>
 8 <h2>EL 的运算符</h2>
 9 <c:set value="mike" var="username" scope="request" />
10 <table border="1" width="50%" align="left">
11 <TR>
12 <TR>
13 <TH>运算式</TH>
14 <TH>结果</TH>
15 </TR>
16 <TR><TD>14 + 3</TD><TD>${14 + 3}</TD></TR>
17 <TR><TD>14 - 3</TD><TD>${14 - 3}</TD></TR>
18 <TR><TD>14 * 3</TD><TD>${14 * 3}</TD></TR>
19 <TR><TD>14 / 3</TD><TD>${14 / 3}</TD></TR>
20 <TR><TD>14 % 3</TD><TD>${14 % 3}</TD></TR>
21 <TR><TD>14 == 3</TD><TD>${14 == 3}</TD></TR>
22 <TR><TD>14 != 3</TD><TD>${14 != 3}</TD></TR>
23 <TR><TD>14 < 3</TD><TD>${14 < 3}</TD></TR>
24 <TR><TD>14 > 3</TD><TD>${14 > 3}</TD></TR>
25 <TR><TD>14 <= 3</TD><TD>${14 <= 3}</TD></TR>
26 <TR><TD>14 >= 3</TD><TD>${14 >= 3}</TD></TR>
27 <TR><TD>true && false</TD><TD>${true && false}</TD></TR>
28 <TR><TD>true || false</TD><TD>${true || false}</TD></TR>
29 <TR><TD>! false</TD><TD>${! false}</TD></TR>
30 <TR><TD>empty username</TD><TD>${empty username}</TD></TR>
31 <TR><TD>empty password</TD><TD>${empty password}</TD></TR>
32 </table>
33 </body>
34 </html>

EL的数学运算符、相等运算符、关系运算符和逻辑运算符就跟其他程序语言一样,并没有特别的地方。但是他的empty运算符就比较特别,为了测试它,笔者谢了这样一样程序代码

1 <c:set value="mike" var="username" scope="request" />

这样Request属性范围里就存在一个名称为username、值为mike的属性。执行此程序时,读者将会发现${empty username}为false;${empty password}为true,其代表的意义就是:它可以在四种属性范围中找到username这个属性,但是找不到password这个属性。

EL Functions

EL函数的语法如下:

  ns:function(arg1,arg2,arg3 ...,argN)

其中ns为前置名称(prefix),它必须和tablib指令的前置名称一样。

如:

<%@ taglib prefix="my"

  uri="http://jakarta.apache.org/tomcat/jsp2-example-tablib" %>

... ...

${my:function(param.name)}

  前置名称都为my,至于function为EL函数的名称,而arg1、arg2等等,都是function的传入值。

当Container执行这段程序时,它会根据uri的值,到web.xml中找到对应的TLD(Tag Library Description)文件。至于web.xml如何设定两者之间的对应,我们一会会说。

 web.xml

 web.xml是每个web站台最主要的设定文件,在这个设定文件中,可以设定许多东西,如:Servlet、resource、Filter等等。不过现在关心的是如何在web.xml中设定taglib的uri是对应到哪个TLD文件。

在web.xml中,<taglib>用来设定标签的TLD文件位置。<taglib-uri>用来指定taglib的uri位置,用户可以自行给定一个uri。

  <taglib-location>用来指定TLD文件的位置。

注意:在定义EL函数时,都必须为公开静态(public static)

JSTL

  JSTL全名为JavaServer Pages Standard Tag Library。JSTL是由JCP(Java Community Process)所指定的标准规格,它主要提供给Java Web开发人员一个标准通用的标签函数库。

  web程序开发人员能够利用JSTL和EL来开发web程序,取代传统在页面上嵌入java程序(Scripting)的做法,以提高程序可读性、维护性和方便性。

JSTL简介

JavaServer Pages Standard Tag Library,它的中文名称为JSP标准标签函数库。JSTL是一个标准的已制定好的标签库,可以应用于各种领域,如:基本输入输出、流程控制、循环、XML文件剖析、数据库查询及国际化和文字格式标准化的应用等。JSTL所提供的标签库主要分为五大类:

(1)核心标签库(Core tag library)

(2)I18N 格式标签库 (I18N-capable formatting tag library)

(3)SQL 标签库 (SQL tag library)
(4)XML 标签库 (XML tag library)
(5)函数标签库 (Functions tag library)

另外,JSTL也支持EL(Expression Language)语法,例如:在一个标准的JSP页面中可能会使用到如下的写法:

<%=userList.getUser().getPhoneNumber() %>

使用JSTL搭配传统写法会变成这样:

<c_rt:out value="<%= userList.getUser().getPhoneNumber()%>"/>

使用JSTL搭配EL,则可以改写成如下的形式:

<c:out value="${userList.user.phoneNumber}"/>

 安装与使用JSTL

必须在Servlet2.4且JSP2.0以上版本的Container才可以使用。JSTL主要有Apache组织的Jakarta Project所实现,要使用先声明:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

这段声明表示我将使用JSTL的核心标签库。一般而言,核心标签库的前置名称(prefix)都为c,当然你也可以自行设定。不过uri此时就必须为http://java.sun.com/jsp/jstl/core

注意:JSTL 1.0中,核心标签库的uri默认为http://java.sun.com/jsp/jstl/core,比JSTL 1.1少一个jsp/ 的路径。因为JSTL 1.1 同时支持JSTL1.0 和1.1,所以假若核心标签库的uri为http://java.sun.com/jsp/jstl/core,则将会使用到JSTL 1.0 的核心标签库。

  接下来使用核心标签库的out标签,显示value的值。${header['User-Agent']}表示取得表头里的User-Agent的值,即有关用户浏览器的种类。

<c:out value="欢迎测试你的第一个使用到JSTL的网页"/>

<c:out value="${header['User-Agent']}"/>

核心标签库(Core tag library)

首先介绍的核心标签库(Core)主要有:基本输入输出、流程控制、迭代操作和URL操作。

在JSP中要使用JSTL中的核心标签库时,必须使用<%@ taglib %>指令,并且设定prefix和uri的值,通常设定如下:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

上述的声明将使用JSTL的核心标签库。

注意:假若没有上述声明指令,将无法使用JSTL的核心功能,这是读者在使用JSTL时必须要小心的地方。

表达式操作

表达式操作分类中包含四个标签:<c:out>、<c:set>、<c:remove>。

<c:out>主要用来显示数据的内容,就像是<%= scripting-language %>一样,例如:

Hello!<c:out value="${username}"/>

语法

语法1:没有本体(body)内容

<c:out value="value"[escapeXml="{true|false}"][defaulet="defaultValue"]/>

语法2:有本体内容

<c:out value="value" [escapeXml="{true|false}"]>
default value
</c:out>

属性:

注意:

表格中的EL字段,表示此属性的值是否可以为EL表达式。

Null和错误处理

  ·假若value为null,会显示default的值;假若没有设定default的值,则会显示一个空的字符串 。

说明

一般来说,<c:out>默认会将<、>、,、”和 &lt;、&gt;、&#039;、&#034; 和 &amp;。
假若不想转换时,只需要设定<c:out>的 escapeXml 属性为 fasle 就可以了

<c:out value="Hello JSP 2.0 !! " />
<c:out value="${ 3 + 5 }" />
<c:out value="${ param.data }" default="No Data" />
<c:out value="<p>有特殊字符</p>" />
<c:out value="<p>有特殊字符</p>" escapeXml="false" />

<c:set>

<c:set>主要用来将变量储存至JSP范围中或是JavaBean的属性中。

语法:

语法1:将value的值储存至范围为scope的varName变量之中

<c:set value="value" var="varName"[scope="{ page|request|session|application }"]/>

语法2:将本体内容的数据存储至scope的varName变量之中

<c:set var="varName" [scope="{ page|request|session|application }"]>
… 本体内容
</c:set>

语法3:

将value的值存储至target对象的属性中

< c:set value="value" target="target" property="propertyName" />

Null 和 错误处理
语法 3 和语法 4 会产生异常错误,有以下两种情况:
☆ target 为 null
☆ target 不是 java.util.Map 或 JavaBean 对象
假若 value 为 null 时:将由储存变量改为移除变量
☆ 语法 1:由 var 和 scope 所定义的变量,将被移除
□ 若 scope 已指定时,则 PageContext.removeAttribute(varName, scope)
□ 若 scope 未指定时,则 PageContext.removeAttribute(varName)
☆ 语法 3:
□ 假若 target 为 Map 时,则 Map.remove(property)
□ 假若 target 为 JavaBean 时,property 指定的属性为 null

说明

使用<c:set>时,var 主要用来存放表达式的结果;scope 则是用来设定储存的范围,例如:假
若 scope="session",则将会把数据储存在 session 中。如果<c:set>中没有指定 scope 时,则它会
默认存在 Page 范围里

注意:var和scope这两个属性不能使用表达式来表示,

<c:set var="number" scope="session" value="${1+1}"/>

把1+1 的结果2存储到number变量中。如果<c:set>没有value属性,此时value之值在<c:set>和</c:set>之间,

<c:set var="number" scope="session">
<c:out value="${1+1}" />
</c:set>

上面的 <c:out value="${1+1}" /> 部分可以改写成 2 或是 <%=1+1%> ,结果都会一样,也就
是说,<c:set>是把本体(body)运算后的结果来当做 value 的值。另外,<c:set>会把 body 中最开头
和结尾的空白部分去掉。如:

<c:set var="number" scope="session">
                       1 + 1
</c:set>

则 number 中储存的值为 1 + 1 而不是 1 + 1。

例子:

<c:set var="number" scope="request" value="${1 + 1}" />
<c:set var="number" scope="session" >
${3 + 5}
</c:set>
<c:set var="number" scope="request" value="${ param.number }" />
<c:set target="User" property="name" value="${ param.Username}" />

1.将 2 存入 Request 范围的 number 变量中;

2.将 8 存入 Session 范围的 number 变量中;
3. 假若 ${param.number}为 null 时, 则移除 Request 范围的 number 变量; 若${param.number}
不为 null 时,则将 ${param.number}的值存入 Request 范围的 number 变量中;
4.假若 ${param.Username}为 null 时,则设定 User(JavaBean)的 name 属性为 null;若不为
null 时,则将 ${param.Username}的值存入 User(JavaBean)的 name 属性(setter 机制)。

<c:remove>主要用来移除变量。

语法

<c:remove var="varName"[scope="{ page|request|session|application }"] />

属性

说明:

<c:remove>必须要有var属性,即要被移除的属性名称,scope则可有可无,例如:

<c:remove var="number" scope="session"/>

  将number变量从Session范围中移除。若我们不设定scope,则<c:remove>将会从Page、Request、Session及application中顺序寻找是否存在名称为number的数据,若能找到时,则将它移除掉,反之则不会做任何的事情。

<c:catch>主要用来处理产生错误的异常情况,并且将错误信息储存起来

语法

<c:catch [var="varName"] >
… 欲抓取错误的部分
</c:catch>

属性:

说明:

<c:catch>主要将可能发生错误的部分放在<c:catch>和</c:catch>之间。如果真的发生错
误,可以将错误信息储存至 varName 变量中

<c:catch var="message">
: //可能发生错误的部分
</c:catch>

另外,当错误发生在<c:catch>和</c:catch>之间时,则只有<c:catch>和</c:catch>之间的程序
会被中止忽略,但整个网页不会被中止。

流程控制

流程控制分类中包含四个标签:<c:if>、<c:choose>、<c:when>和<c:otherwise>

·<c:if>的用途就和我们一般在程序中用的if一样

语法

语法1:没有本体内容(body)

<c:if test="testCondition" var="varName"
[scope="{page|request|session|application}"]/>

语法2:

<c:if test="testCondition" [var="varName"]
[scope="{page|request|session|application}"]>
具体内容
</c:if>

属性

说明:

  <c:if>标签必须要有test属性,当test中的表达式结果为true时,则会执行本体内容;如果为false,则不会执行。

除了test属性之外,<c:if>还有另外两个属性var和scope。当我们执行<c:if>的时候,可以将这次判断的结果存放到属性var里;scope则是设定var的属性范围。

<c:choose>本身只当做<c:when>和<c:otherwise>的父标签

语法

<c:choose>
本体内容( <when> 和 <otherwise> )
</c:choose>

属性

限制

<c:choose>的本体内容只能有:

·空白

·1或多个<c:when>

·0或多个<c:otherwise>

说明

若使用<c:when>和<c:otherwise>来做流程时,两者都必须为<c:choose>的子标签,即:

<c:choose>

<c:when>
</c:when>


<c:otherwise>
</c:otherwise>

</c:choose>

·<c:when>的用途就和我们一般在程序中用的when一样。

语法

<c:when test="testCondition" >
本体内容
</c:when>

限制

<c:when>必须在<c:choose>和</c:when>之间

在同一个<c:choose>中时,<c:when>必须在<c:otherwise>之前

说明

<c:when>必须由test属性,当test中的表达式结果为true时,则会执行本体内容;如果为false时,则不会执行。

·<c:otherwise>

  在同一个<c:choose>中,当所有<c:when>的条件都没有成立时,则执行<c:otherwise>的本体内容。

语法

<c:otherwise>

本体内容

</c:otherwise>

属性

限制

·<c:otherwise>必须在<c:choose>和</c:otherwise>之间

·在同一个<c:choose>中时,<c:otherwise>必须为最后一个标签

说明

  在同一个<c:choose>中,假若所有<c:when>的test属性都不为true时,则执行<c:otherwise>的本体内容。

范例

 1 <c:choose>
 2 <c:when test="${condition1}">
 3 condition1 为 true
 4 </c:when>
 5 <c:when test="${ condition2}">
 6 condition2 为 true
 7 </c:when>
 8 <c:otherwise>
 9 condition1 和 condition2 都为 false
10 </c:otherwise>
11 </c:choose>

范例说明:当 condition1 为 true 时,会显示“condition1 为 true” ;当 condition1 为 false
且 condition2 为 true 时, 会显示 “condition2 为 true” , 如果两者都为 false, 则会显示 “condition1
和 condition2 都为 false” 。

假若 condition1 和 condition2 两者都为 true 时,此时只会显示"condition1 为 true",这是因为
在同一个<c:choose>下,当有好几个<c:when>都符合条件时,只能有一个<c:when>成立。

迭代操作

  迭代(Iterate)操作主要包含两个标签:<c:forEach>和<c:forTokens>。

·<c:forEach>

  <c:forEach>为循环控制,它可以将集合(Collection)中的成员循序浏览一遍。运作方式为当添加符合时,就会持续重复执行<c:forEach>的本体内容。

语法

语法1:迭代一集合对象值所有成员

1 <c:forEach [var="varName"] items="collection" [varStatus="varStatusName"] [begin="begin"]
2 [end="end"] [step="step"]>
3 本体内容
4 < /c:forEach>

语法2:迭代指定的次数

1 <c:forEach  [var="varName"]  [varStatus="varStatusName"]  begin="begin"  end="end"
2 [step="step"]>
3 本体内容
4 </c:forEach>

属性

限制

·假若有begin属性时,begin必须大于等于0

·假若有end属性时,必须大于begin

·假若step属性时,step必须大于等于0

Null和错误处理

·假若items为null时,则表示为一空的集合对象

·假若begin大于或等于items时,则迭代不运算

说明

如果要循环浏览一个集合对象,并将它的内容显示出来,就必须有items属性。

 1 <%@ page contentType="text/html;charset=GB2312 " %>
 2 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 3 <html>
 4 <head>
 5 <title>CH7 - Core_forEach.jsp</title>
 6 </head>
 7 <body>
 8 <h2><c:out value="<c:forEach> 的用法" /></h2>
 9 <%
10 String atts[] = new String [5];
11 atts[0]="hello";
12 atts[1]="this";
13 atts[2]="is";
14 atts[3]="a";
15 atts[4]="pen";
16 request.setAttribute("atts", atts);
17 %>
18 <c:forEach items="${atts}" var="item" >
19 ${item}</br>
20 </c:forEach>
21 </body>
22 </html>

注意:

varName的范围只存在<c:forEach>的本体中,如果超出了本体,则不能再取得varName的值。

  <c:forEach>除了支持数组之外,还有标准J2SE的集合类型,例如:ArrayList、List、LinkedList、Vector、Stack和Set等等;另外还包括java.util.Map类的对象,例如:HashMap、Hashtable、Properties、Provider和Attributes。

  <c:forEach>还有begin、end和step这三种属性:begin主要用来设定在集合对象中开始的位置(注意:第一个位置为0);end用来设定结束的位置;而step则是用来设定现在指到的成员和下一个被指到成员之间的间隔。

1.当begin超过end时 将会产生空的结果

2.当begin虽然小于end但是当两者都大过容器的大小时,将不会输出任何东西

3.最后如果只有end的值超过集合对象的大小,则输出就和没有设定end的情况相同

4.<c:forEach>并不只是用来浏览集合对象而已

<%@ page contentType="text/html;charset=GB2312" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>CH7 - Core_forEach2.jsp</title>
</head>
<body>
<h2><c:out value="<c:forEach> 循环" /></h2>
<c:forEach begin="1" end="10" var="item" >
${item}</br>
</c:forEach>
</body>
</html>

另外,<c:forEach>还提供varStatus属性,主要用来存放现在指到成员的额相关信息。varStatus属性还有另外四个属性:index、count、first和last

·<c:forTokens>用来浏览一字符串中所有的成员,其成员是由定义符号(delimiters)所分隔的

语法

<c:forTokens items="stringOfTokens" delims="delimiters" [var="varName"]
[varStatus="varStatusName"] [begin="begin"] [end="end"] [step="step"]>
本体内容
</c:forTokens>

属性:

限制

·假若有begin属性时,begin必须大于等于0

·假若有end属性时,必须大于begin

·假若有step属性时,step必须大于等于0

Null和错误处理

·假若items为null时,则表示为一空的集合对象

·假若begin大于等于items的大小时,则迭代不运算

说明

  <c:forTokens>的begin、end、step、var和varStatus用法都和<c:forEach>一样,items的内容必须为字符串;而delims是用来分割items中定义的字符串之字符

范例

<c:forTokens items="A,B,C,D,E" delims="," var="item" >
${item}
</c:forTokens>

<%
String phoneNumber = "123-456-7899";
request.setAttribute("userPhone", phoneNumber);
%>
<c:forTokens items="${userPhone}" delims="-" var="item" >
${item}
</c:forTokens>

<c:forTokens items="A,B;C-D,E" delims=",;-" var="item" >
${item}
</c:forTokens>

<c:forEach items="A,B,C,D,E" var="item" >
${item}
</c:forEach>

URL操作

JSTL包含三个与URL有关的标签,它们分别为:<c:import>、<c:redirect>和<c:url>。它们主要的功能是:用来将其他文件的内容包含起来、网页的导向,还有url的产生。

·<c:import>可以把其他静态或动态文件包含至本身JSP网页。它和JSP Action的<jsp:include>最大的差别在于:<jsp:include>只能包含和自己同一个web application下的文件;而<c:import>除了能包含和自己同一个web application下的文件外,亦可包含不同web application或者 是其他网站的文件。

语法

语法1

<c:import url="url" [context="context"] [var="varName"]
[scope="{page|request|session|application}"] [charEncoding="charEncoding"]>
本体内容
</c:import>

语法2:

<c:import url="url" [context="context"]
varReader="varReaderName" [charEncoding="charEncoding"]>
本体内容
</c:import>

属性:

Null和错误处理

·假若url为null或空时,会产生JSPException

说明

  首先<c:import>中必须要有url属性,它是用来设定被包含网页的地址。它可以为绝对地址或是相对地址,使用绝对地址的写法如下:

<c:import url="http://java.sun.com"/>

<c:import>就会把 http://java.sun.com 的内容加到网页中。

另外<c:import>也支持 FTP 协议,假设现在有一个 FTP 站台,地址为 ftp.javaworld.com.tw
它里面有一个文件 data.txt,那么可以写成如下方式将其内容显示出来:
<c:import url="ftp://ftp.cse.yzu.edu.tw/data.txt" />
如果是使用相对地址,假设存在一个文件名为 Hello.jsp,它和使用<c:import>的网页存在于
同一个 webapps 的文件夹时,<c:import>的写法如下:
<c:import url="Hello.jsp" />

如果以“/”开头,那么就表示跳到 web 站台的根目录下,以 Tomcat 为例,即 webapps 目录。
假设一个文件为 hello.txt,存在于 webapps/examples/images 里,而 context 为 examples,可以

写成以下方式将 hello.txt 文件包含进我们的 JSP 页面之中:
<c:import url="images/hello.txt" />
接下来如果要包含在同一个服务器上,但并非同一个 web 站台的文件时,就必须加上 context
属性。假设此服务器上另外还有一个 web 站台,名为 others,others 站台底下有一个文件夹为 jsp,
且里面有 index.html 这个文件,那么就可以写成如下方式将此文件包含进来:
<c:import url="/jsp/index.html" context="/others" />

注意:

被包含文件的 web 站台必须在 server.xml 中定义过, 且<Context>的 crossContext 属性值必须
为 true,这样一来,others 目录下的文件才可以被其他 web 站台调用。
server.xml 的设定范例如下:
: <Context path="/others" docBase="others" debug="0"
reloadable="true" crossContext="true"/>

除此之外,<c:import>也提供 var 和 scope 属性。当 var 属性存在时,虽然同样会把其他文件
的内容包含进来,但是它并不会输出至网页上,而是以 String 的类型储存至 varName 中。scope 则
是设定 varName 的范围。储存之后的数据,我们在需要用时,可以将它取出来,代码如下:
<c:import url="/images/hello.txt" var="s" scope="session" />

我们可以把常重复使用的商标、欢迎语句或者是版权声明,用此方法储存起来,想输出在网页
上时,再把它导入进来。假若想要改变文件内容时,可以只改变被包含的文件,不用修改其他网页。
另外,可以在<c:import>的本体内容中使用<c:param>,它的功用主要是:可以将参数传递给被
包含的文件,它有两个属性 name 和 value

这两个属性都可以使用 EL,所以我们写成如下形式:

<c:import url="http://java.sun.com" >
<c:param name="test" value="1234" />
</c:import>

这样的做法等于是包含一个文件,并且所指定的网址会变成如下:
http://java.sun.com?test=1234

 1 <%@ page contentType="text/html;charset=GB2312" %>
 2 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 3 <html>
 4 <head>
 5 <title>CH7 - Core_import.jsp</title>
 6 </head>
 7 <body>
 8 <h2><c:out value="<c:import> 的用法" /></h2>
 9 <c:set var="input1" value="使用属性范围传到 Core_imported.jsp 中" scope="request"/>包含
10 core_imported.jsp 中<hr/>
11 <c:import url="Core_imported.jsp"charEncoding="GB2312" >
12 <c:param name="input2" value="使用<c:param>传到 Core_imported.jsp 中"/>
13 </c:import><hr/>
14 ${output1}
15 </body>
16 </html>
 1 <%@ page contentType="text/html;charset=GB2312" %>
 2 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 3 <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
 4 <html>
 5 <head>
 6 <title>CH7 - Core_imported.jsp</title>
 7 </head>
 8 <body>
 9 <fmt:requestEncoding value="GB2312" />
10 <c:set var="output1" value="使用属性范围传到 Core_import.jsp 中" scope="request"/>
11 ${input1}</br>
12 <c:out value="${param.input2}" escapeXml="true" />
13 </body>
14 </html>

·<c:url>主要用来产生一个URL

语法

语法1:没有本体内容

<c:url value="value" [context="context"] [var="varName"]
[scope="{page|request|session|application}"] />

语法2:本体内容代表查询字符串(Query String)参数

<c:url value="value" [context="context"] [var="varName"]
[scope="{page|request|session|application}"] >
<c:param> 标签
</c:url>

属性

说明

1 <c:url value="http://www.javafan.net " >
2 <c:param name="param" value="value"/>
3 </c:url>

可以发现<c:url>也可以搭配<c:param>使用,上面执行结果将会产生一个网址为http://www.javafan.net?param=value,我们更可以搭配HTML 的<a>使用,如下:

1 <a href="
2 <c:url value="http://www.javafan.net " >
3 <c:param name="param" value="value"/>
4 </c:url>">Java 爱好者</a>

另外<c:url>还有三个属性,分别为 context、var 和 scope。context 属性和之前的<c:import>
相同, 可以用来产生一个其他 web 站台的网址。 如果<c:url>有 var 属性时, 则网址会被存到 varName
中,而不会直接输出网址。
哪些状况下才会去使用<c:url>?例如:当我们须动态产生网址时,有可能传递的参数不固定,或者是需要一个网址能连至同服务器的其他 web 站台之文件, 而且<c:url>更可以将产生的网址储存
起来重复使用。另外,在以前我们必须使用相对地址或是绝对地址去取得需要的图文件或文件,现
在我们可以直接利用<c:url>从 web 站台的角度来设定需要的图文件或文件的地址,

<img src="<c:url value="/images/code.gif" />" />

如此就会自动产生连到 image 文件夹里的 code.gif 的地址,不再须耗费精神计算相对地址,并
且当网域名称改变时,也不用再改变绝对地址。

● <c:redirect>
<c:redirect>可以将客户端的请求从一个 JSP 网页导向到其他文件。
语法
语法 1:没有本体内容
<c:redirect url="url" [context="context"] />
语法 2:本体内容代表查询字符串(Query String)参数
<c:redirect url="url" [context="context"] > <c:param> </c:redirect >

属性

说明
url 就是设定要被导向到的目标地址,它可以是相对或绝对地址。例如:我们写成如下:
<c:redirect url="http://www.javafan.net" />
那么网页将会自动导向到 http://www.javafan.net。 另外, 我们也可以加上 context 这个属性,
用来导向至其他 web 站台上的文件,例如:导向到 /others 下的 /jsp/index.html 时,写法如下:
<c:redirect url="/jsp/index.html" context="/others" />
<c:redirect> 的功能不止可以导向网页,同样它还可以传递参数给目标文件。在这里我们同样
使用<c:param>来设定参数名称和内容。
范例

 1 <%@ page contentType="text/html;charset=GB2312" %>
 2 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 3 <html>
 4 <head>
 5 <title>CH7 - Core_redirect.jsp</title>
 6 </head>
 7 <body>
 8 <h2><c:out value="<c:redirect> 的用法" /></h2>
 9 <c:redirect url="http://java.sun.com">
10 <c:param name="param" value="value"/>
11 </c:redirect>
12 <c:out value="不会执行喔!!!" />
13 </body>
14 </html>
原文地址:https://www.cnblogs.com/liangxiaoyu/p/5147522.html