Struts2 把所有标签都定义在URI为/struts-tags的命名空间下。可以分为3类:UI标签(主要用于生成HTML元素的标签,可分为表单标签和非表单标签)、非UI标签(主要用于数据访问和逻辑控制等,分为数据标签和控制标签)和Ajax标签(主要用于Ajax支持的标签)。
3.3.1 Struts 2的OGNL表达式
1.OGNL表达式
OGNL(Object Graphic Navigation Language,对象图导航语言),是个开源项目。OGNL是一种功能强大的EL(Expression Language,表达式语言),可以通过简单的表达式来访问Java对象中的属性。
OGNL首先在WebWork项目中得到应用,OGNL是Struts2框架视图默认的表达式语言,OGNL表达式是Struts 2框架的特点之一。
标准的OGNL会设定一个根对象(root对象)。假设使用标准OGNL表达式来求值(不是Struts 2 OGNL),如果OGNL上下文有两个对象,foo对象和bar对象,同时foo对象被设置为根对象(root),则利用下面的OGNL表达式求值。
#foo.blah // 返回foo.getBlah()
#bar.blah // 返回bar.getBlah()
blah // 返回foo.getBlah(),因为foo为根对象
如果要访问的不是根对象,需要使用命名空间,用“#”表示。如果是根对象,则不用指定。
在Struts 2框架中,值栈(Value Stack)就是OGNL的根对象。假设值栈中存在两个对象实例Man和Animal,这两个对象实例都有一个name属性,Animal有一个species属性,Man有一个salary属性。假设Animal在值栈的顶部,Man在Animal后面,如图所示。
一个包含了Animal和Man的值栈
下面的代码片段能更好地理解OGNL表达式。
species // 调用animal.getSpecies()
salary // 调用man.getSalary()
name // 调用animal.getName(),因为Animal位于值栈的顶部
最后一行实例代码返回的是animal.getName()返回值,即返回了Animal的name属性,因为Animal是值栈的顶部元素,OGNL将从顶部元素搜索,所以会返回Animal的name属性值。如果要获得Man的name值,则需要如下代码:
man.name
Struts 2允许在值栈中使用索引,实例代码如下:
[0].name // 调用animal.getName()
[1].name // 调用man.getName()
Struts 2中的OGNL Context是ActionContext,如图所示。
Struts 2的OGNL Context结构示意图
由于值栈是Struts 2中OGNL的根对象。如果用户需要访问值栈中的对象,则可以通过如下代码访问值栈中的属性:
${foo} // 获得值栈中的foo属性
如果访问其他Context中的对象,由于不是根对象,在访问时需要加#前缀。
application对象:用来访问ServletContext,如#application.userName或者#application ["userName"],相当于调用Servlet的getAttribute("userName")。
session对象:用来访问HttpSession,如#session.userName或者#session["userName"],相当于调用session.getAttribute("userName")。
request对象:用来访问HttpServletRequest属性的Map,如#request.userName或者#request["userName"],相当于调用request.getAttribute("userName")。如在3.2.1节中StrutsAction类中代码:
Map request=(Map)ActionContext.getContext().get("request");
request.put("name",getName());
这就是先得到request对象,然后把值放进去,在该例的success.jsp中有:
<s:property value="#request.name"/>
其中#request.name相当于调用了request.getAttribute("name")。
2.OGNL集合操作
使用如下代码直接生成一个List对象:
{e1, e2, e3…}
下面的代码可以直接生成一个Map对象:
#{key: value1, key2: value2, …}
对于集合类型,OGNL表达式可以使用in和not in两个元素符号。其中,in表达式用来判断某个元素是否在指定的集合对象中;not in判断某个元素是否不在指定的集合对象中,代码如下所示:
<s: if test="'foo' in {'foo', 'bar'}">
…
</s: if>
或
<s: if test="'foo' not in {'foo', 'bar'}">
…
</s: if>
除了in和not in之外,OGNL还允许使用某个规则获得集合对象的子集,常用的有以下3个相关操作符。
?:获得所有符合逻辑的元素。
^:获得符合逻辑的第一个元素。
$:获得符合逻辑的最后一个元素。
如下面的代码:
Person.relatives.{?# this.gender=='male'}
该代码可以获得Person的所有性别为male的relatives集合。
3.3.2 数据标签
数据标签属于非UI标签,主要用于提供各种数据访问相关的功能,数据标签主要包括以下几个。
property:用于输出某个值。
set:用于设置一个新变量。
param:用于设置参数,通常用于bean标签和action标签的子标签。
bean:用于创建一个JavaBean实例。
action:用于在JSP页面直接调用一个Action。
date:用于格式化输出一个日期。
debug:用于在页面上生成一个调试链接,当单击该链接时,可以看到当前值栈和Stack Context中的内容。
il8n:用于指定国际化资源文件的baseName。
include:用于在JSP页面中包含其他的JSP或Servlet资源。
push:用于将某个值放入值栈的栈顶。
text:用于输出国际化(国际化内容会在后面讲解)。
url:用于生成一个URL地址。
1. s:property 标签
property标签的作用是输出指定值。property标签输出value属性指定的值。如果没有指定的value属性,则默认输出值栈栈顶的值。该标签有如下几个属性:
default:该属性是可选的,如果需要输出的属性值为null,则显示default属性指定的值。
escape:该属性是可选的,指定是否escape HTML代码。
value:该属性是可选的,指定需要输出的属性值,如果没有指定该属性,则默认输出值栈栈顶的值。该属性也是最常用的,如前面用到的:
<s:property value="#request.name"/>
id:该属性是可选的,指定该元素的标志。
2. s:set 标签
对值栈中的表达式进行求值,并将结果赋给特定作用域中的某个变量名。该标签有如下几个属性:
name:该属性是必选的,重新生成新变量的名字。
scope:该属性是可选的,指定新变量的存放范围。一般为application、session、request、page和action。如果没有指定该属性,则默认放置在值栈中。
value:该属性是可选的,指定赋给新变量的值。如果没有指定该属性,则将值栈栈顶的值赋给新变量。
id:该属性是可选的,指定该元素的引用id。
下面是一个简单例子,展示了property标签访问存储于session中的user对象的多个字段:
<s:property value="#session['user'].username"/>
<s:property value="#session['user'].age"/>
<s:property value="#session['user'].address"/>
由于每次都要重复使用#session['user'],不仅烦人还容易引发错误,更好的做法是定义一个临时变量,使用set标签使得代码易于阅读:
<s:set name="user" value="#session['user'] " />
<s:property value="#user.username"/>
<s:property value="#user.age" />
<s:property value="#user.address" />
3. s:param 标签
param标签主要用于为其他标签提供参数,该标签有如下几个属性:
name:该属性是可选的,指定需要设置参数的参数名。
value:该属性是可选的,指定需要设置参数的参数值。
id:该属性是可选的,指定引用该元素的id。
例如,要为name为fruit的参数赋值:
<s:param name= "fruit">apple</s:param>
或者
<s:param name="fruit" value="apple" />
指定一个名为fruit的参数,该参数的值为apple对象的值,如果该对象不存在,则fruit的值为null。
如果想指定fruit参数的值为apple字符串,则应该这样写:
<s:param name="fruit" value="'apple'" />
4. s:bean 标签
用于创建一个JavaBean实例。创建JavaBean实例时,可以在该标签内使用param标签为该JavaBean实例传入属性。如果要使用param标签传入属性值,则应该为该JavaBean类提供setter方法。如果还希望访问该属性值,则必须为该属性提供getter方法。该标签有如下几个属性:
name:该属性是必选的,用来指定要实例化的JavaBean的实现类。
id:该属性是可选的,如果指定了该属性,则该JavaBean实例会被放入Stack Context中,从而允许直接通过id属性来访问该JavaBean实例。下面是一个简单的例子:
有一个Student类,该类中有name属性,并有其getter和setter方法:
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name=name;
}
}
然后在JSP文件的body体中加入下面的代码:
<s:bean name="Student">
<s:param name="name" value="'zhangsan'"/>
<s:property value="name"/>
</s:bean>
在项目中导入Struts 2的Jar包,再把Student类放在项目的src文件夹下(如果Student类在在某个包下面,那么<s:bean>标签的name要加上包名。例如org.bean.Student),<s:bean>标签内容放在一个JSP文件的body体内,再修改web.xml文件,就可以部署运行该项目,会得到如图所示的界面。
bean标签实例界面
也可以把bean标签的内容改为:
<s:bean name="Student" id="s" >
<s:param name="name" value="'zhangsan'"/>
</s:bean>
<s:property value="#s.name"/>
5. s:action 标签
使用action标签可以允许在JSP页面中直接调用Action。该标签有以下几个属性:
id:该属性是可选的,该属性将会作为该Action的引用标志id。
name:该属性是必选的,指定该标签调用哪个Action。
namespace:该属性是可选的,指定该标签调用的Action所在的namespace。
executeResult:该属性是可选的,指定是否要将Action的处理结果页面包含到本页面。如果值为true,就是包含,false就是不包含,默认为false。
ignoreContextParam:该属性是可选的,指定该页面中的请求参数是否需要传入调用的Action。如果值为false,将本页面的请求参数传入被调用的Action。如为true,不将本页面的请求参数传入到被调用的Action。
6. s:date 标签
date标签主要用于格式化输出一个日期。该标签有如下属性:
format:该属性是可选的,如果指定了该属性,将根据该属性指定的格式来格式化日期。
nice:该属性是可选的,该属性的取值只能是true或false,用于指定是否输出指定日期和当前时刻之间的时差。默认为false,即不输出时差。
name:属性是必选的,指定要格式化的日期值。
id:属性是可选的,指定引用该元素的id值。
nice属性为true时,一般不指定format属性。因为nice为true时,会输出当前时刻与指定日期的时差,不会输出指定日期。当没有指定format,也没有指定nice="true"时,系统会到国际化资源文件中寻找key为struts.date.format的消息,将该消息当成格式化文本来格式化指定日期,如果无法找到,则默认采用DateFormat.MEDIUM格式输出。其用法为:
<s:date name="指定日期取值" format="日期格式"/><!-- 按指定日期格式输出 -->
<s:date name="指定日期取值" nice="true"/><!-- 输出时间差 -->
<s:date name="指定日期取值"/><!—默认格式输出-->
例子:
java
import java.util.Date;
public class Student {
private Date birthday;
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
jsp
<s:bean name="Student"> <s:param name="birthday" value="'2011-12-10'"/> <s:date name="birthday" /><br/> <s:date name="birthday" format="yyyy-MM-dd hh:mm:ss"/><br/> <s:date name="birthday" nice="true"/><br/> </s:bean>
7. s:include 标签
include标签用于将一个JSP页面或一个Servlet包含到本页面中。该标签有如下属性:
value:该属性是必选的,指定需要被包含的JSP页面或Servlet。
id:该属性是可选的,指定该标签的id引用。
用法如下:
<s:include value="JSP或Servlet文件" id="自定义名称"/>
3.3.3 控制标签
属于非UI标签,主要用于完成流程的控制,以及对值栈的控制。控制标签有以下几个:
if:用于控制选择输出的标签。
elseif:用于控制选择输出的标签,必须和if标签结合使用。
else:用户控制选择输出的标签,必须和if标签结合使用。
append:用于将多个集合拼接成一个新的集合。
generator:用于将一个字符串按指定的分隔符分隔成多个字符串,临时生成的多个子字符串可以使用iterator标签来迭代输出。
iterator:用于将集合迭代输出。
merge:用于将多个集合拼接成一个新的集合,但与append的拼接方式不同。
sort:用于对集合进行排序。
subset:用于截取集合的部分元素,形成新的子集合。
1. s:if, s:elseif, s:else标签
这3个标签可以组合使用,但只有if标签可以单独使用,而elseif和else标签必须与if标签结合使用。if标签可以与多个elseif标签结合使用,但只能与一个else标签使用。其用法格式如下:
<s:if test="表达式">
标签体
</s:if>
<s:elseif test="表达式">
标签体
</s:elseif>
<!--允许出现多次elseif标签-->
...
<s:else>
标签体
</s:else>
2. s:iterator 标签
该标签主要用于对集合进行迭代,这里的集合包含List、Set,也可以对Map类型的对象进行迭代输出。该标签的属性如下:
value:该属性是可选的,指定被迭代的集合,被迭代的集合通常都由OGNL表达式指定。如果没有指定该属性,则使用值栈栈顶的集合。
id:该属性是可选的,指定集合元素的id。
status:该属性是可选的,指定迭代时的IteratorStatus实例,通过该实例可判断当前迭代元素的属性。如果指定该属性,其实例包含如下几个方法:
int getCount():返回当前迭代了几个元素。
int getIndex():返回当前被迭代元素的索引。
boolean isEven:返回当前被迭代元素的索引元素是否是偶数。
boolean isOdd:返回当前被迭代元素的索引元素是否是奇数。
boolean isFirst:返回当前被迭代元素是否是第一个元素。
boolean isLast:返回当前被迭代元素是否是最后一个元素。
应用举例:
<%@ page language="java" pageEncoding="utf-8"%>
<%@taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<title>控制标签</title>
</head>
<body>
<table border="1" width="200">
<s:iterator value="{'apple','orange','pear','banana'}"id="fruit" status="st">
<tr <s:if test="#st.even">style="background-color:silver"</s:if>>
<td><s:property value="fruit"/></td>
</tr>
</s:iterator>
</table>
</body>
</html>
通过添加Struts 2必须的Jar包,再建立上面JSP文件,修改web.xml后,就可以部署运行,运行结果如图所示。
iterator标签实例运行结果
3. s:append 标签
用于将多个集合对象拼接起来,组成一个新的集合。必须指定一个id属性,该属性确定拼接的新集合的名称。该标签通过param标签来指定每一个集合,然后把这些集合拼接起来。
应用举例,可以把上例的JSP文件进行修改,其代码为:
<%@ page language="java" pageEncoding="utf-8"%>
<%@taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<title>控制标签</title>
</head>
<body>
<s:append id="newList">
<s:param value="{'apple','orange','pear','banana'}"></s:param>
<s:param value="{'chinese','english','french'}"></s:param>
</s:append>
<table border="1" width="200">
<s:iterator value="#newList" id="fruit" status="st">
<tr <s:if test="#st.even">style="background-color:silver"</s:if>>
<td>
<s:property value="fruit" />
</td>
</tr>
</s:iterator>
</table>
</body>
</html>
部署运行,运行结果如图所示。
append标签实例运行界面
4. s:merge 标签
与append标签的区别,只是拼接方式不同。
假设有2个集合,第一个集合包含3个元素,第二个集合包含2个元素,分别用append标签和merge标签方式进行拼接,它们产生新集合的方式有所区别。下面分别列出:
用append方式拼接,新集合元素顺序为:
第1个集合中的第1个元素
第1个集合中的第2个元素
第1个集合中的第3个元素
第2个集合中的第1个元素
第2个集合中的第2个元素
用merge方式拼接,新集合元素顺序为:
第1个集合中的第1个元素
第2个集合中的第1个元素
第1个集合中的第2个元素
第2个集合中的第2个元素
第1个集合中的第3个元素
代码如下:
<%@ page language="java" pageEncoding="utf-8"%>
<%@taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<title>控制标签</title>
</head>
<body>
<s:merge id="newList2">
<s:param value="{'apple','orange','pear','banana'}"></s:param>
<s:param value="{'chinese','english','french'}"></s:param>
</s:merge>
<table border="1" width="200">
<s:iterator value="#newList2" id="fruit2" status="st2">
<tr <s:if test="#st2.even">style="background-color:silver"</s:if>>
<td>
<s:property value="fruit2" />
</td>
</tr>
</s:iterator>
</table>
</body>
</html>
3.3.4 表单标签
大部分的表单标签和HTML表单元素是一一对应的关系,如下面的代码片段:
<s:form action="login.action" method="post"/>
对应着:
<form action="login.action" method="post"/>
<s:textfield name="username" label="用户名" />
对应着:
用户名:<input type="text" name="username">
<s:password name="password" label="密码"/>
对应着:
密码:<input type="password" name="pwd">
还有下面这种情况,如果有这样一个JavaBean类,类名为“User”,该类中有两个属性:一个是username;另一个是password,并分别生成它们的getter和setter方法,在JSP页面的表单中可以这样为表单元素命名:
<s:textfield name="user.username" label="用户名" />
<s:password name="user.password" label="密码"/>
下面介绍和HTML标签元素不是一一对应的几个重要的表单标签:
1. s:checkboxlist 标签
创建多个复选框。该标签需要指定一个list属性。用法举例:
<s:checkboxlist label="请选择你喜欢的水果" list="{'apple','oranger','pear','banana'}" name="fruit">
</s:checkboxlist>
或者为:
<s:checkboxlist label="请选择你喜欢的水果" list="#{1:'apple',2:'oranger',3:'pear',4:'banana'}" name="fruit">
</s:checkboxlist>
这两种方式的区别:前一种根据name取值时取的是选中字符串的值;后一种在页面上显示的是value的值,而根据name取值时取的却是对应的key,这里就是1、2、3或4。
2. s:combobox 标签
combobox标签生成一个单行文本框和下拉列表框的组合。两个表单元素只能对应一个请求参数,只有单行文本框里的值才包含请求参数,下拉列表框只是用于辅助输入,并没有name属性,故不会产生请求参数。用法举例:
<s:combobox label="请选择你喜欢的水果" list="{'apple','oranger','pear','banana'}" name="fruit">
</s:combobox>
3. s:datetimepicker 标签
datetimepicker标签用于生成一个日期、时间下拉列表框。当使用该日期、时间列表框选择某个日期、时间时,系统会自动将选中日期、时间输出指定文本框中。用法举例:
<s:form action="" method="">
<s:datetimepicker name="date" label="请选择日期"></s:datetimepicker>
</s:form>
注意:在使用该标签时,要在HTML的head部分加入<s:head/>。
4. s:select 标签
select标签用于生成一个下拉列表框,通过为该元素指定list属性的值,来生成下拉列表框的选项。用法举例:
<s:select list="{'apple','oranger','pear','banana'}" label="请选择你喜欢的水果"></s:select>
或者为:
<s:select list="fruit" list="#{1:'apple',2:'oranger',3:'pear',4:'banana'}" listKey="key"listValue="value"></s:select>
5. s:radio 标签
radio标签的用法与checkboxlist用法很相似,唯一的区别就是checkboxlist生成的是复选框,而radio生成的是单选框。用法举例:
<s:radio label="性别" list="{'男','女'}" name="sex"></s:radio>
或者为:
<s:radio label="性别" list="#{1:'男',0:'女'}" name="sex">
</s:radio>
6. s:head 标签
head标签主要用于生成HTML页面的head部分。在介绍<s:datetimepicker>标签时说过,要在head中加入该标签,主要原因是<s:datetimepicker>标签中有一个日历小控件,其中包含了JavaScript代码,所以要在head部分加入该标签。
如果需要在页面中使用Ajax组件,就需要在head标签中加入theme="ajax"属性。
3.3.5 非表单标签
非表单标签主要用于在页面中生成一些非表单的可视化元素。这些标签不经常用到,下面大致介绍一下这些标签:
a:生成超链接。
actionerror:输出Action实例的getActionMessage()方法返回的消息。
component:生成一个自定义组件。
div:生成一个div片段。
fielderror:输出表单域的类型转换错误、校验错误提示。
tablePanel:生成HTML页面的Tab页。
tree:生成一个树形结构。
treenode:生成树形结构的节点。