EL表达式
1.1 为什么需要EL表达式和JSP标签
JSP的产生在一定程度上将Servlet中负责表现的功能抽取了出来,但JSP页内嵌入的Java代码也破坏了页面中负责表现的页面结构,特别是当运算逻辑稍微复杂一点的话,那么JSP页面中大量的Java代码增加了页面维护的难度。
所以使用简单的标签来表现复杂的逻辑以及使用简单的形式表现运算的关系就是EL表达式和JSP标签出现的原因。
1.2. 什么是EL表达式
一套简单的运算规则,用于给JSTL标签的属性赋值,也可以直接用来输出而脱离标签单独使用。
1.3. EL表达式的作用
EL(Expression Language)是从 JavaScript 脚本语言得到启发的一种表达式语言,它借鉴了 JavaScript 多类型转换无关性的特点。
在使用 EL 从scope 中得到参数时可以自动转换类型,因此对于类型的限制更加宽松。 Web 服务器对于 request 请求参数通常会以 String 类型来发送,在得到时使用的 Java 语言脚本就应该是request.getParameter(“XXX”) ,这样的话,对于实际应用还必须进行强制类型转换。而 EL 就将用户从这种类型转换的繁琐工作脱离出来,允许用户直接使用EL 表达式取得的值,而不用关心它是什么类型。
1.4. 访问Bean属性
在JSP页面中经常要输出系统定义的对象的属性,而按照以往的写法需要自己去对象域中获取、转换再输出,使用EL表达式可以非常明显的简化过程。
Bean:指的是一个公共的类,有包,有无参构造,实现以序列化接口,按照固定的方式提供属性的get/set访问方式。针对这种特殊类型的属性访问使用EL表达式实现有两种方式,如下。
方式一:${对象名.属性名}
${user.name}
执行的过程为:从pageContext、request、session、application中依次查找绑定名为“user”的对象,找到后调用“getName”方法,将返回值输出。
假定在session中绑定了一个对象,如下:
User obj = new User(1,“胡萝卜”);
session.setAttribute(“user”,obj);
那么**${user.name}****等价于**下面代码:
<%
User u = (User)session.getAttribute(“user”);
out.print(u.getName();
%>
这种繁琐的取值,转换,输出的过程就都由系统代劳了。而且表达式比以上繁琐代码更会处理null。
如果没有为name属性赋过值,页面输出“”,不会输出null。如果取值时绑定名写错,如**
o
b
j
.
n
a
m
e
,
页
面
也
会
输
出
“
”
∗
∗
,
而
不
是
报
空
指
针
异
常
。
但
属
性
名
写
错
会
报
错
,
如
{obj.name},页面也会输出“”**,而不是报空指针异常。但属性名写错会报错,如
obj.name,页面也会输出“”∗∗,而不是报空指针异常。但属性名写错会报错,如{user.naaa}.
方式二:${对象名[“属性名”]}
表达式也支持属性名的动态读取,这时需要采用方式二**${user[“name”]}**的形式。
假定在Servlet中有如下代码:
User obj = new User(1,”胡萝卜”);
session.setAttribute(“user”,obj);
session.setAttribute(“pName”,”id”);
在JSP中编写如下代码会输出“1”:
${sessionScope.user[“id“]}
在JSP中编写如下代码也会输出“1”:
${sessionScope.user[sessionScope.pName]}
如果pName在绑定时不指定id,而是name,那么这个表达式就会输出“胡萝卜“,所以这种写法可以认为是表达式中有一个变量。sessionScope.pName 等价于 session.getAttribute(“pName”)。
如果User类型的定义如下:
package bean;
public class User {
private String name;
private int age;
private String[] interests;
private String gender;
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String[] getInterests() {
return interests;
}
public void setInterests(String[] interests) {
this.interests = interests;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
那么对于interests这个数组属性的值可以使用如下表达式访问:
${user.interests[0]}
1.5. 指定对象的查找范围
在书写表达式时,如果没有指定搜索范围,那么系统会依次调用pageContext、request、session、application的getAttribute()方法。这样不限定查找范围的代码不利于排错,所以这种取值的操作可以限定对象的查找范围。
如:
${sessionScope.user.name}
一旦指定了对象所在的范围,那么只会在范围内查找绑定对象,不会在找不到的时候再去其他区域中查找了。
sessionScope的位置还可以填写pageScope、requestScope、applicationScope。
1.6. 使用EL表达式进行计算
使用EL表达式可以单独进行运算得出结果并直接输出,如下代码所示,EL进行算术运算,逻辑运算,关系运算,及empty运算。
空运算主要用于判断字符串,集合是否为空,是空或为null及找不到值时都会输出true。
<%request.getSession().setAttribute("sampleValue", new Integer(10));%>
${sessionScope.sampleValue} // 显示 10
${sessionScope.sampleValue + 12} <br> // 显示22
${(sessionScope.sampleValue + 12)/3} <br> // 显示7.3
${(sessionScope.sampleValue + 12) /3==4} <br> // 显示 false
${(sessionScope.sampleValue + 12) /3>=5} <br> // 显示 true
<input type="text" name="sample1" value="${sessionScope.sampleValue + 10}">
// 显示值为20的 Text 控件
${empty null} //显示true
1.7. 使用EL表达式获取请求参数值
以下两种写法分别等价:
**
p
a
r
a
m
.
u
s
e
r
n
a
m
e
与
r
e
q
u
e
s
t
.
g
e
t
P
a
r
a
m
e
t
e
r
(
“
u
s
e
r
n
a
m
e
”
)
;
∗
∗
∗
∗
{param.username} 与 request.getParameter(“username”);** **
param.username与request.getParameter(“username”);∗∗ ∗∗{paramValues.city} 与request.getParameterValues(“city”);**
EL运行原理
JSTL
2.1. 什么是JSTL
Sun 公司 Java 标准规范的 JSTL 由 apache组织负责维护。作为开源的标准技术,它一直在不断地完善。 JSTL 的发布包有两个版本: **Standard-1.0 Taglib **、 Standard-1.1 Taglib ,它们在使用时是不同的。
Standard-1.0 Taglib ( JSTL1.0 )支持 Servlet2.3 和 JSP1.2 规范, Web 应用服务器 **Tomcat4 **支持这些规范,而它的发布也在 Tomcat 4.1.24 测试通过了。
Standard-1.1 Taglib ( JSTL1.1 )支持 Servlet2.4 和 JSP2.0 规范, Web 应用服务器 **Tomcat5 **支持这些规范,它的发布在 Tomcat 5.0.3 测试通过了。
2.2. 如何使用JSTL
将标签库对应的jar包拷贝到WEB-INF/lib目录下,以便于系统可以加载所需要的类。使用taglib指令在页面上引入标签的命名空间和前缀,帮助系统定位对应的类。
如:
<%@taglib uri=“http://java.sun.com/jsp/jstl/core” prefix=“c” %>
JSTL运行原理
2.3. 核心标签
### if标签 ###
JSP页面的显示逻辑中也经常需要进行条件判断,<c:if>标签可以构造简单的**“if-then”**结构的条件表达式,如果条件表达式的结果为真就执行标签体部分的内容。<c:if>标签有两种语法格式:
- 语法1,没有标签体的情况:
<c:if test=“testCondition” var=“varName” [scope="{page|request|session|application}"] /> - 语法2,有标签体的情况,在标签体中指定要执行的内容:
<c:if test=“testCondition” [var=“varName”] [scope="{page|request|session|application}"]>
body content
</c:if>
<c:if>标签的属性说明
如表所示:
属性名 | 是否支持EL | 属性类型 | 属 性 描 述 |
---|---|---|---|
test | true | boolean | 决定是否处理标签体中的内容的条件表达式 |
var | false | String | 用于指定将test属性的执行结果保存到某个Web域中的某个属性的名称 |
scope | false | String | 指定将test属性的执行结果保存到哪个Web域中 |
对于语法2,如果指定了<c:if>标签的scope属性,则必须指定var属性。
使用if标签实现属性判断后的输出。代码如下所示:
<%
User user = new User();
user.setName("胡萝卜");
user.setGender("f");
request.setAttribute("user",user);
%>
姓名:${user.name}<br/>
性别:
<c:if test="${user.gender =='m'}" var="rs" scope="request">男</c:if>
<c:if test="${!rs}">女</c:if>
### choose标签 ###
<c:choose>标签用于指定多个条件选择的组合边界,它必须与**<c:when>和<c:otherwise>标签一起使用**。使用<c:choose>,<c:when>和<c:otherwise>三个标签,可以构造类似 “if-else if-else” 的复杂条件判断结构。
<c:choose>标签没有属性,在它的标签体内只能嵌套一个或多个<c:when>标签和**0个或一个<c:otherwise>**标签,并且同一个<c:choose>标签中的所有<c:when>子标签必须出现在<c:otherwise>子标签之前。
- 如果<c:choose>标签内嵌套一**个<c:when>标签和<c:otherwise>**标签,就相当于“if-else”的条件判断结构;
- 如果<c:choose>标签内嵌套多个<c:when>标签和一个<c:otherwise>标签,就相当于“if-else if-else”标签。
<c:when>标签只有一个test属性,该属性的值为布尔类型。test属性支持动态值,其值可以是一个条件表达式,如果条件表达式的值为true,就执行这个<c:when>标签体的内容。<c:when>标签体的内容可以是任意的JSP代码。<c:otherwise>标签没有属性,它必须作为<c:choose>标签的最后分支出现。
当JSP页面中使用<c:choose>标签时,嵌套在<c:choose>标签内的test条件成立的第一个<c:when>标签的标签体内容将被执行和输出。当且仅当所有的<c:when>标签的test条件都不成立时,才执行和输出<c:otherwise>标签的标签体内容。如果所有的<c:when>标签的test条件都不成立,并且<c:choose>标签内没有嵌套<c:otherwise>标签,则不执行任何操作。
使用choose标签简化多个if标签的判断。代码如下所示:
<%
User user = new User();
user.setName("胡萝卜");
user.setGender("x");
request.setAttribute("user",user);
%>
性别:
<c:choose>
<c:when test="${user.gender == 'm'}">男</c:when>
<c:when test="${user.gender =='f'}">女</c:when>
<c:otherwise>未知</c:otherwise>
</c:choose>
forEach标签
JSP页面的显示逻辑中也经常需要对集合对象进行循环迭代操作,<c:forEach>标签用于对一个集合对象中的元素进行循环迭代操作,或者按指定的次数重复迭代执行标签体中的内容。<c:forEach>标签有两种语法格式
- 语法1,在集合对象中迭代:
<c:forEach [var="varName"]
items="collection"
[varStatus="varStatusName"]
[begin="begin"] [end="end"] [step="step"]>
body content
</c:forEach>
- 语法2,迭代固定的次数:
<c:forEach [var="varName"]
[varStatus="varStatusName"]
begin="begin" end="end" [step="step"]>
body content
</c:forEach>
<c:forEach>标签的属性
属性名 | 是否支持EL | 属性类型 | 属 性 描 述 |
---|---|---|---|
var | false | String | 指定将当前迭代到的元素保存到page这个Web域中的属性名称 |
items | true | 任何支持的类型 | 将要迭代的集合对象 |
varStatus | false | String | 指定将代表当前迭代状态信息的对象保存到page这个Web域中的属性名称 |
begin | true | int | 如果指定items属性,就从集合中的第begin个元素开始进行迭代,begin的索引值从0开始编号;如果没有指定items属性,就从begin指定的值开始迭代,直到end值时结束迭代 |
end | true | int | 参看begin属性的描述 |
step | true | int | 指定迭代的步长,即迭代因子的迭代增量 |
在使用<c:forEach>标签时,需要注意如下几点说明:
- 如果指定begin属性,其值必须大于或等于零;
- 如果指定步长(step属性),其值必须大于或等于1;
- 如果items属性的值为null,则要处理的集合对象为空,这时不执行迭代操作;
- 如果指定的begin属性的值大于或等于集合对象的长度,不执行迭代操作;
- 如果指定的end属性的值小于begin属性的值,不执行迭代操作;
<c:forEach>标签的items属性的值支持下面的数据类型:
任意类型的数组
-
java.util.Collection
-
java.util.Iterator
-
java.util.Enumeration
-
java.util.Map
-
String
-
java.sql.ResultSet
items属性还支持与数据库有关的数据类型java.sql.ResultSet(包括javax.sql.RowSet)。对字符串的迭代操作通常使用<c:forTokens>标签或JSTL函数,例如fn:split和fn:jion。
使用forEach标签完成对集合的遍历输出
使用forEach标签完成对集合的遍历输出。
其中items属性为要遍历的集合,var属性为每次取出来的一个对象,varStatus指定当前迭代的状态。代码如下:
<!-- forEach
Map map = new HashMap();
for(Object itr : interests) {
Integer index = map.get("index");
if(index==null) {
map.put("index",0);
} else {
map.put("index",index+1);
}
map.put("其他","");
}
每次循环JSTL会创建一个对象,用来存储循环次数等值。
可以通过varStatus属性声明该对象的变量,
从而来访问该对象,获取循环次数等值。
-->
<c:forEach items="${stu.interests }"
var="itr" varStatus="s">
${itr },${s.index },${s.count }<br>
</c:forEach>
迭代Collection类型的集合对象
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=gb2312" %>
<%@ page import="java.util.*,org.it315.UserBean" %>
<%
Collection users = new ArrayList();
for(int i=0; i<5; i++)
{
UserBean user = new UserBean();
user.setUserName("user" + i);
user.setPassword("guess" + i);
users.add(user);
}
session.setAttribute("users", users);
%>
<div style="text-align:center">User List
<table border="1">
<tr><td>用户名</td><td>密码</td></tr>
<c:forEach var="user" items="${users}">
<tr>
<td>${user.userName}</td><td>${user.password}</td>
</tr>
</c:forEach>
</table></div>
迭代Map对象
使用<c:forEach>标签迭代Map类型的集合对象时,迭代出的每个元素的类型为Map.Entry,Map.Entry代表Map集合中的一个条目项,其中的getKey()方法可获得条目项的关键字,getValue()方法可获得条目项的值。
EL中的requestScope隐含对象代表request作用域中的所有属性的Map对象,所以我们可以使用<c:forEach>标签迭代输出EL中的requestScope隐含对象中的所有元素,如例所示。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=gb2312" %>
<%
request.setAttribute("attr1","value1");
request.setAttribute("attr2","value2");
%>
<div style="text-align:center">Properties(Map)
<table border="1">
<tr><td>Map的关键字</td><td>Map的对应关键字的值</td></tr>
<c:forEach var="entry" items="${requestScope}">
<tr><td>${entry.key}</td><td>${entry.value}</td></tr>
</c:forEach>
</table></div>
获取迭代的状态信息
不管是迭代集合对象,还是迭代指定的次数,在迭代时都可以获得当前的迭代状态信息。
<c:forEach>标签可以将代表当前迭代状态信息的对象保存到page域中,varStatus属性指定了这个对象保存在page域中的属性名称。
代表当前迭代状态信息的对象的类型为javax.servlet.jsp.jstl.core.LoopTagStatus,从JSTL规范中可以查看到这个类的详细信息,其中定义了如下一些方法:
-
public java.lang.Integer getBegin()
返回为标签设置的begin属性的值,如果没有设置begin属性则返回null -
public int getCount()
返回当前已循环迭代的次数 -
public java.lang.Object getCurrent()
返回当前迭代到的元素对象 -
public java.lang.Integer getEnd()
返回为标签设置的end属性的值,如果没有设置end属性则返回null -
public int getIndex()
返回当前迭代的索引号 -
public java.lang.Integer getStep()
返回为标签设置的step属性的值,如果没有设置step属性则返回null -
public boolean isFirst()
返回当前是否是第一次迭代操作 -
public boolean isLast()
返回当前是否是最后一次迭代操作
下面是一个获取迭代状态信息的例子程序。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=gb2312" %>
<%@ page import="java.util.*,org.it315.UserBean" %>
<%
Collection users = new ArrayList();
for(int i=0; i<6; i++)
{
UserBean user = new UserBean();
user.setUserName("user" + i);
user.setPassword("guest" + i);
users.add(user);
}
session.setAttribute("users", users);
%>
<div style="text-align:center">User List
<table border="1">
<tr><td>用户名</td><td>密码</td><td>index</td>
<td>count</td><td>first?</td><td>last?</td></tr>
<c:forEach var="user" items="${users}" varStatus="sta" begin="1" step="2">
<tr>
<td>${user.userName}</td><td>${user.password}</td>
<td>${sta.index}</td><td>${sta.count}</td>
<td>${sta.first}</td><td>${sta.last}</td>
</tr>
</c:forEach>
</table></div><hr>
<div style="text-align:center">迭代固定的次数
<table border="1">
<tr><td>数值</td><td>index</td><td>count</td>
<td>first?</td><td>last?</td></tr>
<c:forEach var="i" varStatus="sta1" begin="101" end="103">
<tr>
<td>${i}</td><td>${sta1.index}</td><td>${sta1.count}</td>
<td>${sta1.first}</td><td>${sta1.last}</td>
</tr>
</c:forEach>
</table></div>
<c:param>标签
在JSP页面进行URL的相关操作时,经常要在URL地址后面附加一些参数。
<c:param>标签可以嵌套在**<c:import>、<c:url>或<c:redirect>标签内,为这些标签所使用的URL地址附加参数。<c:param>标签在为一个URL地址附加参数时,将自动对参数值进行URL编码**,
例如,
如果传递的参数值为“中国”,则将其转换为“%d6%d0%b9%fa”后再附加到URL地址后面,这也就是使用<c:param>标签的最大好处。<c:param>标签有两种语法格式:
-
语法1,使用value属性指定参数的值:
** <c:param name=“name” value=“value” />** -
语法2,在标签体中指定参数的值:
**
<c:param name=“name”>
parameter value
</c:param>**
<c:param>标签的属性
属性名 | 是否支持EL | 属性类型 | 属性描述 |
---|---|---|---|
name | true | String | 参数的名称 |
value | true | String | 参数的值 |
其它标签我感觉用的比较少就不说了,当用的时候在查找。后面我会写一个如何写自己自定义标签的博客,教大家如何写一个自定义标签。