Struts 2标签简介
与Struts1标签库相比,Struts 2 的标签库有一个巨大的改进之处: Struts2 标签库的标签不依赖于任何表现层技术,也就是说,Struts2 提供的大部分标签,可以在各种表现层技术中使用,包括最常用的JSP 页面,也可以在Velocity 和FreeMarker 等模板技术中使用。
虽然Struts 2 大部分标签可以在所有表现层技术中使用,但也有极少数标签在某些表现层技术中使用时会受到限制,这一点请开发者务必要注意。
Struts 标签分类
虽然Struts2 把所有的标签都定义在URI 为“/struts-tags”的空间下,但我们依然可以对Struts2 标签进行简单的分类。
Struts2 可以将所有标签分成如下三类:
- UI (User Interface,用户界面) 标签: 主要用于生成HTML 元素的标签。
- 非UI 标签: 主要用于数据访问、逻辑控制等的标签。
- Ajax 标签: 用于Ajax (Asynchronous JavaScript And XML) 支持的标签。
- 表单标签: 主要用于生成HTML 页面的form 元素,以及普通表单元素的标签。
- 非表单标签: 主要用于生成页面上的树、Tab 页等标签。
- 流程控制标签: 主要包含用于实现分支、循环等流程控制的标签。
- 数据访问标签: 主要包含用于输出ValueStack 中的值、完成国际化等功能的标签。
使用Struts标签库
在使用的时候,还是需要添加这一指令。
<%@taglib prefix="s" uri="/struts-tags"%>
在上面标签中,因为该标签以“s”作为前缀,故该标签需要使用URI 为/struts-tags 的标签库处理,通过前缀关联,系统知道从Struts 2 标签库中寻找名为abc 的标签来处理相应标签。
OGNL
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,他是一个开源项目。Struts框架使用OGNL作为默认的表达式语言。
在Struts2 应用中,视图页面可通过标签直接访问Action 属性值( 实际上这只是一种假想,类似于Web 应用application.session.request和page 四个范围的“银行”一样,Struts2 自行维护一个特定范围的“银行”, Action 将数据放入其中,而JSP 页面可从其中取出数据,表面上似乎JSP 可直接访问Action 数据),当Action属性不是简单值(基本类型值或String类型值) 时,而是某个对象,甚至是数组、集合时,就需要使用表达式语言来访问这些对象、数组、集合的内部数据了, Struts 2 利用OGNL 表达式语言来实现这个功能。
在传统的OGNL 表达式求值中,系统会假设只有一个“根”对象。下面是标准OGNL 表达值,如果系统的StackContext 中包含两个对象: foo 对象,它在Context 中的名字为foo; bar对象,在Context 中的名字为bar,并将foo 对象设置成Context 的根对象。
//返回foo.getBlah () 方法的返回值,属性一般都有set、get方法。
#foo.blah
//返回bar.getBlah() 方法的返回值
#bar.blah
//因为foo 是根对象,所以默认是取得foo对象的blah属性
blan
如果需要访问的属性属于根对象,则可以直接访问该属性,如blah; 否则必须使用一个对象名作为前缀修饰该属性,如#bar.blah。
Struts 2 使用标准的Context 来进行OGNL 表达式语言求值,OGNL 的顶级对象是一个Context,这个Context 对象就是一个Map 类型实例,根对象有是ValueStack对象。
存放在ValueStack里面可以通过${属性名}(el表达式),直接获取。
值栈的简单定义:
- 简单的说,值栈是对应每一个请求对象的轻量级的数据存储中心,在这里统一管理着数据,供Action、Result、Interceptor等Struts2的其他部分使用,这样数据被集中管理起来而不凌乱。
- 当有请求的时候,Struts2会为每个请求创建一个新的值栈,也就是说,值栈和请求是一一对应的,不同的请求,值栈也不一样, 而值栈封装了一次请求所有需要操作的相关的数据。
- 正是因为值栈和请求的对应关系,因此值栈能保证线程安全的为每个请求提供公共的数据存取服务。
- 就是ROOT根对象,ognl访问值栈不用加任何的访问前缀,只需action中声明相应的属性,并且生成属性对应的set和get方法,页面中通过struts2标签就可以存放/取出值栈中的值,EL表达式${username}如果没有加访问范围,访问的也是值栈,这只是最简单的值栈应用
- 值栈的特点:如果访问的值栈里有多个对象,且相同的属性在多个对象中同时出现,则值栈会按照从栈顶到栈底的顺序,寻找第一个匹配的对象。
参考学习:这里写链接内容
- parameters 对象:用于访问HTTP 请求参数。例如#parameters[‘foo]或#parameters.foo,用于返回调用HttpServletRequest的getParameter(“foo”)方法的返回值。
- request 对象:用于访问HttpServletRequest 的属性。例如#request [foo]或#request.foo,用于返回调用HttpServletRequest的getAttribute(“foo”)方法的返回值。
- session 对象:用于访问HttpSession的属性。例如#session[‘foo’]或#session.foo,用于返回调用HttpSession的getAttribute(“foo”)方法的返回值。
- application 对象:用于访问ServletContext的属性。例如#application[foo]或#application.foo,用于返回调ServletContext的getAtribute(“foo”) 方法的返回值。
- attr对象:该对象将依次搜索如下对象;PageContext.HttpServletRequest、HttpSession、ServletContext中的属性。
注意:OGNL 的Stack Context 是整个OGNL 计算、求值的Context,而ValueStack 只是StackContex 内的“根”对象而已。OGNL 的Stack Context里除了包括ValueStack 这个根之外,还包括parameters.request.session.application.attr 等命名对象,但这些命名对象都不是根。Stack Context “根”对象和普通命名对象的区别在于:
- 访问Stack Context 里的命名对象需要在对象名之前添加# 前缀;
- 当访问OGNL 的Stack Context 里“根”对象的属性时,可以省略对象名。
OGNL集合运算
创建List类型集合的语法:
{e1,e2,e3....}
创建Map类型集合的语法:
#{key1:value1,key2:value2....}
多个元素之间用英文隔开。
对于集合,OGNL 提供了两个运算符: in 和notin,其中in 判断某个兀素是含在指定集合中; not in 则用于判断某个元素是否不在指定集合中。
除此之外,OGNL 还允许通过某个规则取得集合的子集。取得子集时有如下三个操作符:
- ? 取出所有符合选择逻辑的元素。
- ^取出符合选择逻辑的第一个元素。
- $: 取出符合选择逻辑的最后一个元素。
person.relatives.{? #this.gender== ‘male’} ,直接在集合后紧跟.{ }运算符表明用于取出该集合的子集,在{ }内使用?表明取出所有符合选择逻辑的元素,而#this 代表集合里元素。因此,上面代码的含义是: 取出person 的所有性别为male 的relatives (亲戚) 集合。
OGNL表达式对静态属性的访问
为了让OGNL对静态属性的设置,需要在Struts.xml中进行设置。
OGNL 表达式可以通过如下语法来访问静态成员:
<constant name="struts.oqn1.allowStatiaMethodAccess" value="true"></constant>
@classNameQstaticField
@className@staticMethod(val...)
<s:property value="@java.lang.Math@PI"></s:property>
控制标签
- if: 用于控制选择输出的标签。
- elself/elseif: 与if 标签结合使用,用于控制选择输出的标签。else: 与if 标签结合使用,用于控制选择输出的标签。
- append: 用于将多个集合拼接成一个新的集合。
- generator:它是一个字符串解析器,用于将一个字符串解析成一个集合。iterator: 这是一个迭代器,用于将集合迭代输出。
- merge: 用于将多个集合拼接成一个新的集合。但与append 的拼接方式有所不同。sort: 这个标签用于对集合进行排序。
- subset: 这个标签用于截取集合的部分元素,形成新的子集合。
if/else if/else
这三个标签可以组合使用,只有<…>标签可以单独使用,后面的
<s:set value="90" var="age"/>
<s:if test="#age>60">老年人</s:if>//test是对条件
<s:elseif test="#age>35">中年人</s:elseif>
<s:else>年轻人</s:else>
<s:debug></s:debug>
我们通过可以看到它把该属性添加到Stack Context中了。
iterator
iterator 标签主要用于对集合进行迭代,这里的集合包含List、Set 和数组,也可对Map 集合进行迭代输出。使用
<table border="2px">
<s:iterator value="{'laoqiang','laoqiangtest','laoqiangtest1'}" var="name" status="st">//value构造的集合是通过OGNL来创建,var是给迭代器起的名字,方便你去引用。
<tr <s:if test="#st.odd">style="background-color:#bbbbbb"</s:if>>//通过设置迭代的的索引奇数,来设置行的背景颜色。
<td>
<s:property value="#st.count"/>
<s:property value="name"></s:property>
</td>
</tr>
</s:iterator>
</table>
<s:append var="newlist1">
<s:param value="#{'name':'laoqiang','age':'12','sex':'man'}"></s:param>
<s:param value="#{'test1','test3','test2'}"></s:param>
</s:append>
<table>
<s:iterator value="#newlist1" status="sttt1" var ="new2">
<tr>
<td>
<s:property value="key"></s:property></td>
<td>
<s:property value="value"/>
</td>
</tr>
</s:iterator>
</table>
在该例子中,我们展示了在对map集合的合并,与遍历显示,同时我们还需要知道,在map中,如果没有value,只有key,那么显示的时候,就按照key显示。
generator
使用generator标签可以将指定字符串按指定分隔符分隔成多个子串,临时生成的多个子串可以使用iterator 标签来迭代输出。可以这样理解: generator将一个字符串转化成一个Iterator 集合。在该标签的标签体内,整个临时生成的集合将位于ValueStack 的顶端,但一旦该标签结束,该集合将被移出ValueStack。
- count : 该属性是一个可选的属性,该属性指定生成集合中元素的总数。
- separator: 这是一个必填的属性,该属性指定用于解析字符串的分隔符。
- val: 这是一个必填的属性,该属性指定被解析的字符串。
- converter: 这是一个可选的属性,该属性指定一个转换器,该转换器负责将集合中的每个字符串转换成对象,通过该转换器可以将一个字符串解析成对象集合。该属性值必须是一个org.apache.Struts.util.lteratorGenerator.over 对象。
- var: 这是 一个可选的属性,如果指定了该属性,则将生成的Iterator 对象放入StackContext中。该属性也可替换成id,但推荐使用var 属性。
<s:generator separator="," val="'laoqiang,laoqiange,laoqiangt'" var="books">
<s:debug></s:debug>
<table border="1px">
<s:iterator>
<tr>
<td>
<s:property></s:property>
</td>
</tr>
</s:iterator>
</table>
</s:generator>
${requestScope.books}
如果指定了var 属性,就可将生成的集合放入Struts 2 的Stack Context 中(实际上还会设置成request 范围的属性)。
Struts2 的很多标签都与该标签类似,它们都可以指定var (以前是id )属性,一旦指定了var 属性,则会将新生成、新设置的值放入Stack Context 中(必须通过#name 形式访问); 如果不指定var属性, 则新生成、新设置的值不会放入Stack Context 中, 因此只能在该标签内部访问新生成、新设置的值一一此时新生成、新设置的值位于ValueStack中, 因此可以直接访问。
merge
该标签和append一样的功能,都是将多个集合合并成一个集合,但是区别就在于添加的顺序不同。
<s:merge var="list" >
<s:param value="{'laoqiang','laoqiang1','laoqiang2'}"></s:param>
<s:param value="{'laoqiang3','laoqiang4','laoqiang5'}"></s:param>
</s:merge>
</table>
<s:iterator value="#list" var="list1">
<tr>
<td>
<s:property value="#list1"></s:property>
</td>
</tr>
</s:iterator>
</table>
subset
subset标签用于取得集合的子集。
- count: 这是一个可选属性,该属性指定子集中元素的个数。如果不指定该属性,则默认取得源集合的全部元素。
- source:这是一个可选属性,该属性指定源集合。如果不指定该属性,则默认取得ValueStack栈顶的集合。
- start: 这是一个可选属性,该属性指定子集从源集合的第几个元素开始截取。默认从第一个元素(即start 的默认值为0) 开始截取。
- decider:这是一个可选属性,该属性指定由开发者自己决定是否选中该元素。该属性必须指定一个org.apache.struts2.utilSubsetteratorFiler.Decidr对象。
- var: 这是- 一个可选属性,如果指定了该属性,则将生成的Iterator 对象设置成page 范围的属性。
该属性也可替换成id,但推荐使用var 属性。
在subset 标签内时,subset 标签生成的子集合放在ValueStack 的栈顶,所以我们可以在该标签内直接迭代该标签生成的子集合。如果该标签结束后,该标签生成的子集合将被移出ValueStack栈。
<s:subset source="{'laoqiang','laoqiang1','laoqiang2','laoqiang3','laoqiang4','laoqiang5'}" start="1" count="4">
<table border="2px">
<s:iterator>
<tr>
<td><s:property></s:property></td>
</tr>
</s:iterator>
</table>
</s:subset>
我们可以自己定义截取子集的规则:
public class MyDe implements Decider{
@Override
public boolean decide(Object arg0) throws Exception {
// TODO Auto-generated method stub
String s = (String)arg0;
return s.endsWith("3");
}
}
<s:bean var="myde" name="com.example.test.MyDe"></s:bean> //通过var把这个bean对象添加statck context中,自己可以用<s:debug/>看。
<s:subset source="{'laoqiang','laoqiang1','laoqiang2','laoqiang3','laoqiang4','laoqiang5'}" start="1" count="4" decider="#myde">//在Stack Context中获取非根对象。
<table border="2px">
<s:iterator>
<tr>
<td><s:property></s:property></td>
</tr>
</s:iterator>
</table>
</s:subset>
<s:debug></s:debug>
- comparator : 这是- 一个必填的属性,该属性指定进行排序的Comparator 实例。
- source : 这是一个可选的属性, 该属性指定被排序的集合。如果不指定该属性, 则对ValueStack 栈顶的集合进行排序。
- var: 这是一个可选的属性, 如果指定了该属性, 则将生成的Iterator 对象设置成page 范围的属性,不放入StackContext 中。该属性也可替换成id,但推荐使用var 属性。该属性的作用与subset 标签中var 属性的作用相同。
public class MyCom implements Comparator {
@Override
public int compare(Object o1, Object o2) {
// TODO Auto-generated method stub
return o2.toString().length()-o1.toString().length();
}
}
<s:bean var="mycom" name="com.example.test.MyCom"></s:bean>
<s:sort source="{'laoqiasdng','laoqiasdsdsng1','laoqiasdsdng2','laoqisdsdsdsang3','sdsdsd','laoqiandssg5'}" comparator="#mycom">
<table>
<s:iterator>
<tr>
<td><s:property/></td>
</tr>
</s:iterator>
</table>
</s:sort>
数据标签
- action: 该标签用于在JSP 页面直接调用一个Action,通过指定executeResult 参数,还可将该Action的处理结果包含到本页面中来。
- bean: 该标签用于创建一个JavaBean 实例。如果指定了var 属性,则可以将创建的JavaBean实例放入StackContext 中。
- date: 用于格式化输出一个日期。
- debug: 用于在页面上生成一个调试链接,当单击该链接时,可以看到当前ValueStack 和Stack Context 中的内容。
- i18n: 用于指定国际化资源文件的baseName.
- include: 用于在JSP 页面中包含其他的JSP 或Servlet 资源。
- param: 用于设置一个参数,通常是用做bean标签、ur|标签的子标签。
- push : 用于将某个值放入ValueStack 的栈顶。
- text: 用于输出国际化消息。
- url: 用于生成一个URL 地址。
- property: 用于输出某个值,包括输出ValueStack、Stack Context 和Action Context 中的值。
Action
在Struts 2中提供了在Jsp中直接调用Action。
- var: 这是一个可选属性,一旦定义了该属性, 该Action 将被放入ValueStack 中, 该属性可用id代替,但推荐使用var。
- name : 这是一个必填属性,通过该属性指定该标签调用哪个Action。
- namespace : 这是一个可选属性,该属性指定该标签调用的Action所在的namespace。这里的namespace要和你在Struts.xml中填写的一致,否则会出问题。
- executeResult: 这是一一个可选属性,该属性指定是否要将Action的处理结果页面包含到本页面。该属性值默认值是false, 即不包含。
- ignoreContextParams:这是一个可选参数,它指定该页面中的请求参数是否需要传入调用的Action。该参数的默认值是false,即将本页面的请求参数传入被调用的Action。
public class TagAction extends ActionSupport {
private String username;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String execute1() throws Exception{
ActionContext.getContext().put("username",getUsername());//在这直接获取url中的username参数。
return "DONE";
}
<s:action name="tag1" executeResult="true" namespace="/" />
<s:action name="tag2" executeResult="true" namespace="/" ignoreContextParams="false"/>ignoreContextParams设置为true,是不接受任何参数到Action。
采用Get方法携带参数:http://localhost:8889/StrutsTag/index.jsp?username=helloword,注意这里的username,要和Action类的属性一致。
Bean
bean 标签用于创建一个JavaBean 实例。创建JavaBean 实例时,可以在该标签体内使用
<s:bean name="com.example.test.bean.Person">//这里的javabean是存放在ValueStack
<s:param name="name" value="'laoqiang'"></s:param>//**注意这里的字符你需要加单引号。**
<s:param name="age" value="12"></s:param>
<s:property value="'laoqiang'"/>
<s:property value="12"/>
</s:bean>
<s:bean name="com.example.test.bean.Person" var="personbean">//通过var将bean存放在stackcontext中,可以在<s:bean>外面去获取它的值。同时也存放在requestScope
<s:param name="name" value="'laoqiang'"></s:param>
<s:param name="age" value="12"></s:param>
</s:bean>
<s:property value="#personbean.name"/>
<s:property value="#personbean.age"/>
Date
该标签主要用于显示时间。
- format: 这是一个可选属性, 如果指定了该属性, 将根据该属性指定的格式来格式化日期。
- nice: 这是一个可选属性,该属性只能为true或者false,它用于指定是否输出指定日期和当前时刻之间的时差。该属性默认是false,即表示不输出时差。
- name : 这是一个必填属性,该属性指定要格式化的日期值。
- var: 这是一个可选属性,如果指定了该属性,格式化后的字符串将被放入Stack Context 中,并放入requestScope 中,但不会在页面上输出。该属性也可用id 来代替,但推荐使用var。
<s:bean var="date" name="java.util.Date">//必须要导入这个javabean对象
</s:bean>
<s:date name="#date" format="yy/MM/dd"/></br>
<s:date name="#date" nice="true"/></br>
<s:date name="#date"/>//如果nice是默认false,并且没有指定格式,是按照默认的struts时间格式显示,这个修改可以到struts的常量配置修改
<s:date name="#date" var="time"/></br>//如果你指定var,是不显示时间的,需要你通过s:property配置显示。
<s:property value="#time"/></br>//下面的显示都是按照默认的
${requestScope.time}
debug
该标签主要用于调试ValueStack和Stack Context的值的变化。
include
将jsp或者Servlet导入到该文件中。
<s:include value="index1.jsp">
<s:param name="username" value="'laoqiang'"></s:param>//可以传参数给包含的Jsp页面。
<s:debug/>
</s:include>
${param.username}
param
该标签在Struts中主要可以携带参数,用法在之前都已经用过。这个标签携带的参数是get传递。
其中,需要注意的传入的是字符串,要在字符串的外面添加单引号。
push
该标签将某个值放到ValueStack的栈顶,从而更简单的访问该值。
只有在push 标签内时,被push 标签放入ValueStack 中的对象才存在;一旦离开了push 标签,则刚刚放入的对象将立即被移出ValueStack。
- value 该属性是指定要放入ValueStack的值。
<s:bean name="com.example.test.bean.Person" var="person">
<s:param name="name" value="'laoqiang'"/>
<s:param name="age" value="123"/>
</s:bean>
//主要将一个javabean放入到StackContext,在将它放入到ValueStack中
<s:push value="#person">
<s:property value="name"/>
<s:property value="age"/>
</s:push>
Set
set 标签用于将某个值放入指定范围内,例如application 范围、 session范围等。当某个值所在对象图深度非常深时,例如有如下的值: person.worker.wife.pareag 每次访问该值不仅性能低下,而且代码可读性也差。为了避免这个问题,可以将该值设置成一个新值,并放入特定的范围内。
set 标签可以理解为定义一个新变量,且将一个已有的值复制给新变量,并且可以将新变量放到指定的范围内,原来对于Stack Context中,el表达式是不可以直接访问的。set 标签用于生成一个新变量,并且把该变量放置到指定范围内,这样就允许直接使用JSP2 表达式语言来访问这些变量了,当然也可通过Struts2 标签来访问它们。
- scope: 这是- 一个可选属性,指定新变量被放置的范围,该属性可以接受application,session、request.
page 或action 5个值。该属性默认值是action。 - value: 这是- 一个可选属性,指定将赋给变量的值。如果没有指定该属性,则将ValueStack栈顶的值赋给新变量。
- var: 这是一个可选属性,如果指定了该属性,则会将该值放入ValueStack 中。
当把指定值放入特定范围时,范围可以是application、session.request.page 和action 5 个值,前面4 个范围很容易理解; 如果指定action 范围, 则该值将被放入request 范围中,并被放入OGNL 的Stack Context 中。
<s:bean name="com.example.test.bean.Person" var="person">
<s:param name="name" value="'laoqiang'"></s:param>
<s:param name="age" value="1234"></s:param>
</s:bean>
<s:set value="#person" var="newperson"/>//默认就是作用范围是action,也就是request中
<s:property value="#newperson.name"/>
${requestScope.newperson.name}//注意el表示访问某个对象的属性方式
${requestScope.person.name}//可以用set的新名字去访问,也可以用旧的名字去访问。
url
url 标签用于生成一个URL 地址,可以通过为url 标签指定param子元素,从而向指定URL 发送请求参数。
- action: 这是一个可选属性,指定生成URL 的地址为哪个Action,如果Action 不提供,就使用value 作为URL
的地址值。要想跳转实际你定义的action,你需要把Struts.xml注册的action的name,填写在这。 - anchor: 这是一个可选属性,指定URL 的锚点。
- encode: 这是一个可选属性,指定是否需要对参数进行编码,默认是true.
- escapeAmp: 这是一个可选参数,指定是否需要对&符号进行编码,默认是true。
- forceAddSchemeHostAndPort: 这是一个可选参数,指定是否需要在URL
对应的地址里强制添加scheme、主机和端口。 - includeContext: 这是一个可选属性,指定是否需要将当前上下文包含在URL 地址中。
- includeParams :这是一个可选属性, 该属性指定是否包含请求参数,该属性的属性值只能为none、get
或者all。该属性值默认是get。
属性值为get时,该url会将访问其所在jsp的的请求的所有get方法的参数添加到自身来。
属性值为all时更是将get和post的的参数值全部添加到自身来。
一般我们并不需要额外的参数,所以定义为none。 - method: 这是一个可选属性,该属性指定Action 的方法。当我们用Action 来生成URL时,如果指定了该属性,则URL将链接到指定Action 的特定方法。要想跳转实际你定义的action,你需要把Struts.xml注册的action的name,填写在这。
- namespace: 这是一个可选属性,该属性指定命名空间。当我们用Action 来生成URL 时,如果指定了该属性,则URL
将链接到此namespace 的指定Action 处。 - portletMode: 这是一个可选属性,指定结果页面的portlet 模式。
- scheme : 这是一个可选属性,用于设置scheme 属性。
- value: 这是一个可选属性,指定生成URL 的地址值,如果value 不提供就用action 属性指定的Action 作为URL
地址。 - var: 这是一个可选属性,如果指定了该属性,将会把该链接值放入Struts 2 的ValueStack中。该属性可用id
代替,但l推荐使用var。 - windowState: 这是一个可选属性,指定结果页面的portlet 的窗口状态。
<s:url action="laoqiang">
<s:param name="name" value="'helloword'"/> /StrutsTag/laoqiang.action?name=helloword
</s:url>
</br>
<s:url value="laoqiang">
<s:param name="name" value="'helloword'"/>laoqiang?name=helloword 根据这个你应该看出value和action都是作为地址值,但是有所不同
</s:url>
</br>
<s:url>
<s:param name="name" value="'helloword'"/>/StrutsTag/index.jsp?name=helloword
</s:url>
</br>
<a href="<s:url includeParams="get" method="execute1" action="tag2" forceAddSchemeHostAndPort="true"/>"> fsdfdsfs</a>
//我们通过<s:url/>来创造一个url,放到<a href></a>就可以跳转了
property
property 标签的作用就是输出指定值。property 标签输出value 属性指定的值,如果没有指定value属性,则默认输出ValueStack 栈顶的值。
- default: 这是一个可选属性,如果需要输出的属性值为null,则显示default 属性指定的值。
Struts 2的模板和主题
对于一个JSP 页面里包含的UI 标签而言,既可以直接设置该UI 标签需要使用的模板, 也可以设置该UI 标签使用的主题。实际上,对于界面开发者而言,并不推荐直接设置模板属性。因为模板是以主题的形式组织在一起的,界面开发者应该选择特定主题,而不是强制使用特定模板来表现一个UI标签。模板是一个UI 标签的外在表示形式。
主题是模板的组织形式,模板被包装在主题里面,对于开发者应该是透明的。当需要使用特定模板来表现某个UI 标签时,应让主题来负责模板的加载。
设置主题方式
- 通过设定特定UI 标签上的theme 属性来指定主题。
- 通过设定特定UI 标签外围的Form 标签的theme 属性来指定主题。
- 通过取得page 会话范围内以theme 为名称的属性来确定主题。
- 通过取得session 会话范围内的命名为theme 的属性来确定主题。
- 通过取得application 会话范围内的命名为theme 的属性来确定主题。
- 通过设置名为struts.ui.theme 的常量(默认值是xhtml) 来确定默认主题,该常量可以在struts.properties
文件或者struts.xml 文件中确定。
上面的几种指定特定UI 标签主题的方式,它们是有着不同优先级的,排在前面的方式会覆盖排在后面的方式。例如,
我们通过特定UI 标签上的theme 属性指定了该标签的主题,那么后面指定的主题将不会起作用。
Struts 2 元素属性
JavaScript 拥有属性
鼠标悬停的通用属性
css设置属性
表单元素的name于value属性
每一个表单元素,都会映射成一个Action的属性,所以在使用的一定要保证表单元素的name属性值和对应Action的属性值一致,而一旦Action被实例化之后,Action的属性值就是表单元素的value。
表单元素
- checkboxlist
checkboxlist 标签可以一次创建多个复选框,用于同时生成多个的HTML 标签。它根据list 属性指定的集合来生成多个复选框,因此,使用该标签指定一个list 属性。
listKey: 该属性指定集合元素中的某个属性(例如集合元素为Person 实例,指定Person 实例的name 属性) 作为复选框的value.如果集合是Map,则可以使用key 和value 值指定Map 对象的key 和value 作为复选框的value.
listValue: 该属性指定集合元素中的某个属性(例如集合元素为Person 实例,指定Person实例的name 属性) 作为复选框的显示的选项名。如果集合是Map,则可以使用key 和value 值指定Map 对象的key 和value 作为复选框的标签。
<s:checkboxlist name="a" list="{'laoqinag1','laoqiang2','laoqiang3'}" labelposition="top" />
<s:checkboxlist name="b" list="#{'name':'laoqiang','age':23,'sex':'man'}" listKey="value" listValue="key"/>
<s:bean name="com.example.test.service.BookService" var="bs"/>//通过var,将这个bean添加到StatckContext中
<s:debug/>
<s:checkboxlist name="c" list="#bs.book" listKey="name" listValue="price"/>
上面的集合创建,包括方法的调用都是基于OGNL的,OGNL访问方法,该方法名必须是get开头,调用的时候去除get,其他小写。如果你构建集合,不是一个对象,就没必要指定listKey,listValue,就比如第一种。
public class BookService {
public Book[] getbook(){
return new Book[]{
new Book("laohe",21),
new Book("laohe1",22),
new Book("laohe2",23),
new Book("laohe3",24),
};
}
}
- doubleselect
doubleselect 标签会生成个级联列表框(会生成两个下拉列框),当选择第一个下拉列表框时,第: 二个下拉列表框的内容会随之改变。
<s:set var="bs11" value="#{'person': {'name','age','sex'},'teacher':{'laoqiang','teacherage'},'stuname':{'laopi','stuage'}}"/>
<s:form>
<s:doubleselect name ="d" list="{'laohe','laoqiang'}" doubleList="top=='laohe' ? {'laohe1','laohe2'} : {'laoqqiag1','laoqiang2'}" doubleName="e"/>
//top 是第一个框中的选中的值,通过#bs11[top],map的键来获取。
<s:doubleselect name="f" size="3" list="#bs11.keySet()" doubleList="#bs11[top]" doubleSize="3" doubleName="g"/>
</s:form>
第一个下拉列表框只支持两组值,如果第一个下拉列表框包含三个或更多的值,这在默认情况下,
里的list 和doubleList 属性就不能这样直接设定了。我们采用的是map。
- optiontransferselet
optiontransferselet 会生成两个列表选择框,并生成系列的按钮用于控制各选项在两个下拉列表之间的移动、升降等。当提交该表单时,两个列表选择框对应的请求参数都会被提交。
<s:optiontransferselect label="你喜欢的图书" name="optbook" leftTitle="中国图书" rightTitle="外国图书" list="{'java','wp','ss','po'}"
multiple="true" addAllToLeftLabel="全部向左移动" selectAllLabel="全部选择" addAllToRightLabel="全部向右移动" doubleList="{'ppp','www',,'rrrr','tttt'}"
doubleName="rightbook" doubleMultiple="true"
/>
这个标签都是封装好的,你直接对照属性使用就好。
- select
该标签是生成一个下拉列表,基本用法和上面doubleselect 一致。
- 列表内容
radio 标签的用法与checkboxlist 的用法几乎完全相同,一样可以指定label.list、listKey 和listValue等属性。与checkboxlist 唯一不同的是,checkboxlist 生成多个复选框,而radio 生成多个单选钮。
<s:radio name="u" list="{'laohe','loaqinag','jiusjb','plkninb'}"/>
//如果是普通的集合无需指定listkey和listvalue
- optgroup
optgroup 标签用于生成一个下拉列表框的选项组,因此,该标签必须放在selec…标签中使用。一个下拉列表框中可以包含多个选项组,因此可以在一个select…>标签中使用多个
<s:form>
<s:select name="rr" list="{'laohe','laoqiang','laoki','laopo'}" >
<s:optgroup label="选项组" list="#{'name':'laohe','sex':'man'}" listKey="key" listValue="value">
</s:optgroup>
<s:optgroup label="选项组1" list="#{'name1':'laohe1','sex1':'man1'}" listKey="key" listValue="value">
//label属性是设置选项组名字的,不可以选中哦
</s:optgroup>
</s:select>
</s:form>//下拉需要放在form中
token
这是一个用于防止多次提交表单的标签,token标签能阻止多次提交表单的问题(避免刷新页面导致的多次提交)。如果需要该标签起作用,则应该在Struts2 的配置文件中启用TokenInterceptor拦截器或TokenSessionStorel nterceptor 拦截器。token 标签的实现原理是在表单中增加一个隐藏域,每次加载该页面时,该隐藏域的值都不相同。TokenInterceptor 拦截器则拦截所有用户请求,如果两次请求时该token 对应隐藏域的值相同(前一次提交时token 隐藏域的值保存session 里),则阻止表单提交。
默认情况下,token 标签生成的隐藏域的name 为struts.token.不要在表单中因此,另外再定义一个名为struts.token 的表单域。
<package name="default" extends="struts-default" namespace="/">
<action name="token" class="com.example.test.action.TestToken">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="token"/>
<result name="invalid.token">/refresh.jsp</result>
<result name="success">/show.jsp</result>
</action>
</package>
上面配置文件中使用了2 个拦截器,其中defaultStack 是系统默认的拦截器栈。看似前面Struts2 应用都没有使用该拦截器,实际上该拦截器默认会生效; 此处因为显式使用了token 拦截器,所以必须显式配置使用defaultStack 拦截器,否则它不会默认生效。关于拦截器介绍请看下一章内容。另: 如果表单页没有使用标签,则千万不要使用token 拦截器,否则它将导致无法提交表单。
updownselect
updownselect 标签的用法非常类似于select 标签的用法,区别是该标签生成的列表框可以上下移动选项。因此使用该标签时,一样可以指定list.listKey 和listValue 等属性,这些属性的作用与使用select 标签时指定的list、listKey 和listValue 等属性完全相同。
<s:form>
<s:updownselect name="a" labelposition="top" label="请选择" moveUpLabel="向上移动" moveDownLabel="向下移动" list="{'laoqiang','laoqiang2','laoqiang3'}"/>
<s:updownselect name="b" labelposition="top" label="请选择2" moveUpLabel="向上移动" emptyOption="true" moveDownLabel="向下移动" list="#{'name':'laohe','age':'23','sex':'man'}" listKey="key" listValue="value"/>
<s:bean name="com.example.test.service.BookService" var="bs2"></s:bean>
<s:updownselect name="c" labelposition="top" label="请选择3" moveUpLabel="向上移动" emptyOption="true" moveDownLabel="向下移动" list="#bs2.book" listKey="key" listValue="value"/>
</s:form>
注意在使用StackValue中的对象实例作为list集合是不需要加{ }的,加{ }的OGNL表达式构造的集合。
emptyOption 在列表构造一个空的选项。
非表单标签
非表单标签主要用于在页面中生成一些非表单的可视化元素,例如Tab 页面、输出HTML页面的树形结构等。当然,非表单标签也包含在页面中显示Action 里封装的信息。
- actionerror: 如果Action 实例的getActionErrors()方法返回不为null,则该标签负责输出该方法返回的系列错误。
- actionmessage: 如果Action实例的getActionMessages()方法返回不为null,则该标签负责输出该方法返回的系列消息。
- component: 使用此标签可以生成一个自定义组件。
- fielderror: 如果Action 实例存在表单域的类型转换错误、校验错误,该标签则负责输出这些。
public class TestActionMessage extends ActionSupport {
@Override
public String execute() throws Exception {
// TODO Auto-generated method stub
addActionError("错误信息1");//可以看出添加信息就类似在集合中添加数据,在显示的时候,集合是通过有序列表进行显示。
addActionError("错误信息2");
addActionMessage("这是普通消息1");
addActionMessage("这是普通消息2");
return SUCCESS;
}
@Override
public Collection<String> getActionErrors() {
// TODO Auto-generated method stub
return super.getActionErrors();
}
@Override
public Collection<String> getActionMessages() {
// TODO Auto-generated method stub
return super.getActionMessages();
}
}
配置action
<action name="testmessage" class="com.example.test.action.TestActionMessage">
<result name="success">/show.jsp</result>//name属性要和配置的actionname一致。
</action>
<s:action name="testmessage" executeResult="true" namespace="/"></s:action>//executeResult是action1执行结果包含在本页面中。
action中ignoreContextParam:可选参数,指定该页面的请求参数是否需要传入调用的Action中,默认值是false,
namespace 是防止你在配置多个package下的action重名问题。
component
component 标签可用于创建自定义视图组件,这是一个非常灵活的用法。如果开发者经常需要使用某个效果片段, 就可以考虑将这个效果片段定义成一个视图组件,然后在页面中使用component 标签来使用该自定义组件。
- theme : 自定义组件所使用的主题, 如果不指定该属性, 则默认使用xhtml 主题。
- templateDir: 指定自定义组件的主题目录,如果不指定, 则默认使用系统的主题目录, 即template 目录。
template: 指定自定义组件所使用的模板。
除此之外,还可以在cmponent 标签内使用param子标签,子标签表示向该标签模板中传入额外的参数。如果希望在模板中取得该参数,总是采用: parameters.paramNam或者 p a r a m e t e r s . p a r a m N a m 或 者 parameters[param形式。
下面我们先来说第一种模板,采用默认系统的主题目录,系统的模板文件你可以把struts-core 核心的jar解压,就可以看到在一个template/xhtml (xhtml 是代表的是主题)
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%@taglib prefix="s" uri="/struts-tags"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<div>
<s:debug/>
<s:select list="parameters.list1">//这个下拉列表的数据是通过list1来传进来的
</s:select>
</div>
<s:component template="MyTemplate.jsp" >
<s:param name="list1" value="{'laoeh2','laohe3','laohe4'}"/>
</s:component>
在这里我特别需要说的就是你编写的目录路径,必须和系统的那个命名一致,且必须放置在webroot下,也就是根目录,绝对不可以是其他路径,否则web是加载不到模板文件,因为采用系统的主题,它会到指定的template/xhtml下检索。
如果采用自定义主题,那就easy多了。
<s:component templateDir="templateDir" theme="customtheme" template="MyAnotherTemplate.jsp" >
<s:param name="list" value="{'laoeh6','laohe8','laohe9'}"/>
</s:component>
<div>
<select>
<s:iterator value="%{top.parameters.list}">//因为参数会添加到栈值中,top取栈顶元素
<option><s:property/></option>
</s:iterator>
</select>
</div>
<s:component templateDir="templateDir" theme="customtheme" template="MyAnotherTemplate.jsp" >
<s:param name="list" value="{'laoeh6','laohe8','laohe9'}"/>
</s:component>
这回你把模板文件随便放在哪多行,但是要在webroot这个根目录下,其他路径下,需要添加对应的路径。
需要注意在使用自定义模板的时候,需要创建两个文件夹,一个对应templateDir,子文件夹对应theme。
可以看出这种方式灵活,但是也有缺点,就是使用自定义主题,原有的struts的主题,路径都不会加载,在上面的代码中无法使用了。