6.2 使用通用标签
Struts2的标签库比以前版本有很大进步,不再仅依赖于JSP标签,还包括FreeMarker和Velocity。标签可以分为两类:通用标签和UI标签(也称为HTML标签)。在使用的时候与使用JSP自定义标签相同,第一步就是要导入标签库。Struts2的引用方法简单,如下:
<%@ taglib prefix="s" uri="/struts-tags"%>
而在Struts1.x版本中,标签库是分离的,要分别导入:
<%@ taglib uri="struts-html.tld" prefix="html"%> <%@ taglib uri="struts-bean.tld"prefix="bean"%> <%@ taglib uri="struts-logic.tld"prefix="logic"%>
通用标签用于在页面被渲染的时候控制流程。它们也可以用于从Action和Value Stack之外提取数据,比如Internationalization、JavaBeans,以及包含额外的url或者action执行结果。
注意
Struts2提供的标签库是不依赖于显示层技术的。大多数的标签都可以用于所有的模板语言(如JSP、Velocity和FreeMarker),但是有一些只能用于特定的模板语言。标签库可以在哪些模板应用与开发这个标签库使用的是哪种语言这两个概念是不同的。
6.2.1 标签语法
Struts2的标签语法非常容易理解,所有的属性最初都是被设置为字符串的,然后它们会解析 %{ }语法,任何大括号之间的内容都会基于value stack求值。注意3个规范:
1)所有String类型属性都会解析%{ }中间的字符。
2)所有非字符串类型属性都不会解析,但是会直接被当做一个OGNL表达式求值。
3)对第二个规则的例外情况是,如果非字符串属性以“%{”开始并以“}”结束,这些字符会在对表达式求值之前被截取出来。
最简单的例子是下面的解释标签语法如何工作的例子。这个例子仅仅演示了规则1)。
<s:textfield label="%{getText("state.label")}" name="state"/>
在这个例子里,label被动态求值,设置为OGNL表达式getText("state.label")的结果,这个表达式会调用国际化装置来获取i18n主键state.label的值。name是一个字符串属性,被简单地设置为“state”字符串。下面的例子演示了规则2)。
<s:select label="%{getText("state.label")}" name="state" multiple="true"/>
这个例子看起来与前一个例子类似,要注意的主要事情是“multiple”属性是布尔类型的,这意味着它符合规则2),因为“true”会被当做OGNL表达式求值得到true,符合条件。
现在假设要扩展这个例子来演示规则3),让multiple属性变成动态的:
<s:select label="%{getText("state.label")}" name="state" multiple="%{allowMultiple}"/>
因为这个属性是布尔类型的,并且以规则3)中正确的字符开始和结束,它变成表达式allowMultiple,并会在value stack上进行求值,返回一个true或者false,就像前一个例子一样。
下面的例子多半是不正确的:
<s:textfield label="%{getText("state.label")}" name="state" value="CA"/>
这个例子只有在如果表达式CA会产生某些东西时才能工作,也就是说程序中的action有一个getCA()方法,这大概不是设计者所期望的。这是因为value属性是Object,因此规则2)会被应用。如果期望设置一个静态的字符串作为初始值,需要提供一个OGNL表达式来返回一个字符串。例如,这是来完成它的正确方式:
<s:textfield label="%{getText("state.label")}" name="state" value="%{'CA'}"/>
同时也可以设置value属性仅仅为'CA'。
说明
推荐使用被解析表达式的方式,因为将来当Struts2支持解析所有类型的属性时,代码会继续工作。
6.2.2 控制标签
控制标签(Control Tags)提供操纵集合以及有条件地生成内容的功能,与Struts1.x中的逻辑标签功能基本相同,但使用方法简单许多。它包括7个标签。
1)if、elseif、else属于条件控制标签,用来控制内容生成与否。这3个标签经常一起使用,使用方法与编程语言中的用法很类似。表6.6列出了条件标签的参数描述,实例6-3给出一个组合使用的例子,不符合判断条件的<div>不会出现在网页中。
表6.6 if标签参数
【实例6-3】条件标签:TestLib.jsp
01 <%@ taglib prefix="s" uri="/struts-tags"%> 02 03 <html> 04 <body> 05 <s:if test="false"> <!--当判断为假时--> 06 <div> 07 条件为假, 不会出现 08 </div> 09 </s:if> 10 <s:elseif test="true"> <!--当判断为真时--> 11 <div> 12 条件为真, 会出现 13 </div> 14 </s:elseif> 15 <s:else> 16 <div> 17 不会出现 18 </div> 19 </s:else> 20 </body> 21 </html>
图6.4 浏览页面
【运行程序】浏览该页面,结果如图6.4所示。
【代码剖析】在上述代码中,首先会根据表达式“test=""”的值来决定执行哪一句,即不符合条件的<div>不会出现在网页里。执行顺序为if标签、elseif标签和else标签。
2)iterator标签的作用是迭代处理一个java.util.Connection或者java.util.Iterator对象。表6.7列出了条件标签的参数描述。
表6.7 iterator标签参数
一个简单的例子,迭代一个名称为days的list,打印每一个元素的属性。
<s:iterator value="days"> <p>day is: <s:property/></p> </s:iterator>
一般情况下迭代标签往往结合表格输出来展现数据。实例6-4展示了迭代标签与条件标签混合使用生成表格,如果行数是奇数,则背景色为灰色并且输出相应内容。
【实例6-4】iterator标签:TestLib1.jsp
01 <html> 02 <body> 03 <table> <!--定义循环的集合--> 04 <s:iterator value="{'Spring','JPA','WebWork','Struts2'}" id="cjgong" 05 status="stat"> 06 <tr <s:if test="#stat.first">style="background- 07 color:#c0c0c0"</s:if> <!--当记录为第一行时--> 08 <s:elseif test="#stat.last">style="background- 09 color:#ff0000"</s:elseif>> <!--当记录为最后一行时--> 10 <td> <!--判断是奇数还是偶数--> 11 这个迭代次数是一个 12 <s:if test="#stat.odd">奇数</s:if> 13 <s:elseif test="#stat.even">偶数 14 </s:elseif> 15 <s:else>出错</s:else> 16 </td> 17 <td> <!--输出当前索引--> 18 当前迭代索引为 19 <s:property value="#stat.index" /> 20 </td> 21 <td> <!--输出当前次数--> 22 当前迭代次数是第 23 <s:property value="#stat.count" /> 24 次 25 </td> 26 <td> 27 迭代值为 28 <s:property value="cjgong" /> 29 </td> 30 </tr> 31 </s:iterator> 32 </table> 33 </body> 34 </html>
图6.5 浏览页面
【运行程序】浏览该页面,结果如图6.5所示。【代码剖析】在上述代码中,标签<iterator>首先遍历属性value的值并创建了名为stat的status,然后通过stat的first属性判断当前迭代元素是否是第一个元素,如果是则设置背景颜色为灰色;通过stat的last属性判断当前迭代元素是否是最后一个元素,如果是则设置背景颜色为红色。在具体输出相应信息时,首先通过odd属性判断当前迭代元素的引用是否为奇数,通过even属性判断当前迭代元素的引用是否为偶数;通过index属性获取当前迭代元素的索引,通过count属性获取已经迭代元素的个数,
3)sort标签的作用是对一个可以迭代的对象进行排序操作。sort标签往往与iterator标签嵌套使用,也可以当做iterator的修饰标签。表6.8给出了sort标签的参数描述。实例6-5中用sort标签对一个名称为“myList”的列表进行了排序,然后又用iterator标签循环打印。
表6.8 sort标签参数
【实例6-5】 sort标签:TestLib2.jsp
01 <html> 02 <body> 03 <s:bean id="testComparator" name="cjgong.TestComparator"/> 04 <table> <!--定义了排序策略--> 05 <s:sort comparator="#testComparator" 06 source="{'cjgong','Jilin','xiaoming'}" id="testSort"> 07 <s:iterator> <!--输出相应的值--> 08 <s:property /> 09 </s:iterator> 10 </s:sort> 11 </table> 12 </body> 13 </html>
【运行程序】浏览该页面,结果如图6.6所示。
图6.6 浏览页面
【代码剖析】在上述代码中,标签<sort>中属性source指定了需要排序的集合,而属性comparator指定了排序的策略。
在上述页面用到了一个关于排序策略的类TestComparator,其具体内容如实例6-6所示。
【实例6-6】排序策略:TestComparator.java
public class TestComparator implements Comparator { public int compare(Object o1, Object o2) { //如果第一个字符串中字符"n"的位置比第二个字符串靠前就返回一个负值 return ((String)o1).indexOf("n")-((String)o2).indexOf("n"); } }
【代码剖析】在上述代码中,实现了一个根据字符“n”的位置来排序的策略。
4)generator标签的作用是产生一个迭代器,表6.9给出了generator标签的参数描述。
表6.9 generator标签参数
实例6-7给出的例子是如何生成一个迭代器,但是在生成的迭代器中只有3个元素可以使用,分别命名为a1、b2和c3,这是因为count属性被设置为3。
【实例6-7】generator标签:TestLib3.jsp
01 <html> 02 <body> <!--定义迭代的集合--> 03 <s:generator separator="," val="'cjgong,Java,Struts2,Spring'"> 04 <s:iterator> 05 <s:property /> <!--输出相应的元素--> 06 </s:iterator> 07 </s:generator> 08 <br> 09 </body> 10 </html>
【代码剖析】在上述代码中,以“,”符号作为分隔符将一个字符串进行分隔。
5)append标签也是用来做iterator标签的辅助,将不同iterator中的内容合并为一个iterator。表6.10给出了append标签的参数描述。
表6.10 append标签的参数
6.2.3 数据标签
数据标签(Data Tags)用来提供各种数据相关的功能。范围从显示一个action的直接结果,到获取本地化的数值等。
1. property标签
property标签也许是Struts2中最常用的标签了,它实现的功能也是很简单的:输出OGNL表达式的值。表6.11给出property标签支持的属性。如果Value属性没有指定,那么将会被设定为top,也就是返回位于值栈最顶部的对象,注意value的数据类型不是String,所以它会自动进行求值。
如果需要输出的内容经过HTML的转义,就需要设置escape属性。在默认情况下,有property标签打印出来的数值是没有经过转义的。
表6.11 property标签的参数
2. set标签
set标签赋予变量一个特定范围内的值。当希望给一个变量赋一个复杂的表达式时,每次访问该变量而不是复杂的表达式时用到。其在两种情况下非常有用:复杂的表达式很耗时(性能提升)或者很难理解(代码可读性提高)。set标签的参数如表6.12所示。
表6.12 set标签的参数
如下代码中每次都要使用#session['user'],代码冗长而且容易出错。
<s:property value="#session['user'].username"/> <s:property value="#session['user'].age"/> <s:property value="#session['user'].address"/>
这里可以使用set标签定义临时变量:
<s:set name="user" value="="#session['user']"> <s:property value="#user.username"/> <s:property value="#user.age"/> <s:property value="#user.address"/>
3. push标签
set标签可以在action context中放置数据,而push标签则可以将对象的引用压入值栈中。如果围绕某单个对象做大量操作,则是相当有用的。可以将对象压入值栈之后直接对其进行操作,而不需要在每个与对象管理的表达式之前加上对象名字。表6.13所示是push标签的参数表。
<!--push 标签的使用--> <s:set name="user" value="="#session['user']"> <s:push value="#user"> <s:property value="username"/> <s:property value="age"/> <s:property value="address"/> <s;push>
表6.13 push标签的参数
push可以理解为更加直接的set操作,其把一个变量直接放入到值栈顶部,而不需要再使用变量名来引用,在一定范围内可以直接调用其属性。push不能针对表达式操作。图6.7所示为push和set的区别。
说明
很多时候一个页面往往只是围绕一个对象来展开的,push标签也是基于此产生。
4. bean标签
实例化一个符合JavaBeans规范的class,标签体内可以包含几个Param元素,用于调用setter方法给此class的属性赋值。如果设定了id属性,则该实例将会放到值栈中。bean标签的参数如表6.14所示。
图6.7 set和push标签行为示意图
表6.14 bean标签的参数
下面的例子初始化了一个叫SimpleCounter的class实例并且给其foo属性赋了值(setFoo('BAR')),然后该实例被压进Valuestack,也就是这时可以通过Property标签调用其getter方法(getFoo())取到相应的值。
<s:bean name="com.opensymphony.webwork.example.counter.SimpleCounter" id="counter"> <s:param name="foo" value="BAR"/> </s:bean>
上例中把id设为counter,导致该实例会被放进stack的context中。可以通过标签取到该实例:
<s:property value="#counter" name="foo"/>
5. action标签
通过指定命名空间和action名称,该标签允许在JSP页面直接调用action标签体用来渲染action执行结果,除非设定了executeResult参数为true,否则在struts.xml中为该action指定的Result Processor不会执行,这个值默认是false。action标签的参数如表6.15所示。
表6.15 action标签的参数
举个例子来说明action标签的应用。实例6-8是action类,在struts.xml中的配置如实例6-9所示。
【实例6-8】Action类:S_Action.java
01 import com.opensymphony.xwork2.Action; 02 public class S_Action { 03 String cjgong; //定义字符串变量 04 public String getCjgong() { //设置相应属性的getter和setter方法 05 return cjgong; 06 } 07 public void setCjgong(String cjgong) { 08 this.cjgong = cjgong; 09 } 10 public String testAction() { //编写testAction()方法 11 return Action.SUCCESS; 12 } 13 }
【代码剖析】在上述代码中,首先定义了一个字符串变量cjgong,然后直接在testActon()方法中返回SUCCESS字符串。
【实例6-9】Action配置:struts.xml
01 <?xml version="1.0" encoding="UTF-8" ?> 02 <!DOCTYPE struts PUBLIC 03 "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" 04 "http://struts.apache.org/dtds/struts-2.0.dtd"> 05 <struts> 06 <package name="cjgong" extends="struts-default"> 07 <action name="sAction" class="cjgong.S_Action" method="testAction"> 08 <result>/success.jsp</result> 09 </action> 10 </package> 11 </struts>
【代码剖析】在上述代码中,配置执行成功后转向页面success.jsp。
使用标签<action>发送请求的页面代码,如实例6-10所示。
【实例6-10】发送请求页面:action.jsp
01 <%@ page contentType="text/html; charset=utf-8"%> 02 <%@ taglib prefix="s" uri="/struts-tags"%> 03 <html> 04 <body> 05 在页面中显示结果页的action标签: 06 <br /> 07 <s:action name="sAction" executeResult="true" /> 08 <br /> 09 在页面中显示结果页并对参数进行阻止的action标签: 10 <br /> 11 <s:action name="sAction" ignoreContextParams="true" 12 executeResult="true" /> 13 <br /> 14 不在页面中显示结果页的action标签: 15 <br /> 16 <s:action name="sAction" /> 17 <br /> 18 </body> 19 </html>
【代码剖析】在上述代码中,分别使用了3个action标签<action>,对于第一个该标签,由于设置了属性executeResult,所以在页面中显示结果;对于第二个该标签,由于设置了属性ignoreContextParams,所以在页面中拦截了参数;对于第三个该标签没进行设置,所以在页面什么都不显示。
关于成功的页面如实例6-11所示。
【实例6-11】成功页面:success.jsp
01 <%@ page language="java" " pageEncoding="utf-8"%> 02 <html> 03 <head> 04 <title>succee</title> 05 </head> 06 <body> 07 <% <!--输出相应信息--> 08 System.out.println("succee.jsp"); 09 %> 10 This is Result page. 11 <br> 12 </body> 13 </html>
【运行程序】当在地址栏里输入http://localhost: 8080/struts2tag/action.jsp?cjgong=test地址时,就会显示出图6.8所示的页面。
说明
在这里例子中使用“!default”参数,Struts2在action中找不到default方法,会去执行“doDefault”方法。
图6.8 action标签举例界面图
6.2.4 杂项标签
本节介绍几种杂项标签,它们都是为一些特殊需求设计的。
1. include标签
include标签与JSP的<jsp:include>标签的功能类似,在使用Struts2进行开发的时候,Struts2的include标签会更有用。这里有以下两个原因:
1)它能够更好地与Struts2集成在一起。很多时候include进来的文件名是一个变量,而Struts2的include标签提供了对值栈进行本地访问的能力,并且提供了更具有扩展性的参数模型。
2)Struts2 的include标签同样也是对用户友好的。如果希望包含../index.jsp这样的URL,仍然可以随意地这样做,即使一些应用服务器并不支持这种类型的URL。可以规避很多<jsp:include>的限制,因为Struts会将这些URL都重写为绝对路径的URL。
2. url标签
该标签用于创建url,可以通过“param”标签提供request参数。url标签的参数如表6.16所示。
表6.16 url标签的参数
注意
当includeParams的值是'all'或者'get',param标签中定义的参数将有优先权,也就是说其会覆盖其他同名参数的值。实例6-12展示了url标签的几种用法。
【实例6-12】url标签:url.jsp
01 <%@ page contentType="text/html; charset=utf-8"%> 02 <%@ taglib prefix="s" uri="/struts-tags"%> 03 04 <html> 05 <body> 06 <s:url value="success.jsp"></s:url> <!--关于属性value的使用--> 07 <br /> 08 <s:url action="sAction"></s:url> <!--关于属性action的使用--> 09 <br /> 10 <s:url action="sAction"> <!--关于属性param的使用--> 11 <s:param name="cjgong" value="'java'"></s:param> 12 </s:url> 13 <br /> 14 </body> 15 </html>
图6.9 浏览页面
【运行程序】浏览该页面,结果如图6.9所示。
【代码剖析】在上述代码中,分别使用了3个标签<url>,对于第一个该标签,通过属性value设置URL请求地址;对于第二个该标签,通过属性action设置URL的请求地址;对于第三个该标签,还通过参数param设置了请求地址的参数。
3. i18n标签和text标签
text标签是为了支持国际化信息的标签,用于显示特定语言的文本,譬如中文。它是基于键查询的标签。i18n标签将某个特定“resource bundle”放入value stack。然后通过text标签拿到相应message,而不是仅限于绑定到当前action的bundle。实例6-13所示为text和i18n标签的用法。
【实例6-13】国际化标签:i18n.jsp
01 <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> 02 <%@ taglib prefix="s" uri="/struts-tags"%> 03 <html> 04 <body> 05 <s:i18n name="Chinese"> <!--关于国际化标签的使用--> 06 i18n标签内部:<s:text name="cjgong" /> 07 <br> 08 </s:i18n> 09 i18n标签外部: 10 <s:text name="cjgong" /> 11 </body> 12 </html>
【运行程序】浏览该页面,结果如图6.10所示。
【代码剖析】上述代码如果想正确运行,必须创建两个属性文件Chinese.properties和English.properties。
图6.10 浏览页面
Chinese.properties cjgong=\u5E38\u5EFA\u529F English.properties cjgong=changjiangong
4. param标签
param标签在前文中已经几次用到,它是为其他标签提供参数,如include标签和bean标签。该标签的两个属性如表6.17所示。
表6.17 param标签的参数
参数的name属性是可选的,如果提供,会调用Component的方法addParameter(String,Object);如果不提供,则外层嵌套标签必须实现UnnamedParametric接口(如TextTag)。
param标签有两种用法:
<param name="color">blue</param> <--(A)--> <param name="color" value="blue"/> <--(B)-->
注意
param的两种用法有细微的差别,(A)中,参数值会以String的格式放入statck。(B)中该值会以java.lang.Object的格式放入statck。