标签
内容
- 使用数据标签
- 使用控制标签控制流程
- 其他标签
- OGNL表达式语言
1 入门
所有Struts2的领域数据,都保存在ValueStack中,而ValueStack和一些基础性的数据,都保存在ActionContext对象中。
1.1 ActionContext 和 OGNL
我们使用OGNL表达式将表单字段名绑定到对象的具体属性。action对象被放在ValueStack对象,OGNL表达式指向这个栈上的属性。实际上 OGNL可以用来求更宽泛的值的对象为ActionContext。
ActionContext包含了框架的请求处理过程中可以访问的所有数据。
img
ActionContext包含了丰富的数据,其中最重要的是ValueStack。其他的数据都对应着Servlet API中的概念。
名字 | 描述 |
---|---|
parameters | 当前请求中请求参数的映射 |
request | 请求作用域的属性的映射 |
session | 会话作用于的属性的映射 |
application | 应用程序作用域的属性和映射 |
attr | 按照页面、请求、会话、应用程序作用于的顺序,返回第一个出现的属性 |
ValueStack | 包含当前请求的应用程序特定领域的所有数据 |
为OGNL选择根对象
OGNL表达式必须选择ActionContext中的一个对象作为它的根对象。
#session[‘user’]
OGNL通过#操作符主动命名为ActionContext中的会话映射。
1.2 虚拟对象ValueStack
当Struts 2接收到一个请求时,它立即创建一个ActionContext、一个ValueStack和一个动作对象。
如图,action的name由于是最底层的模型对象,所以他不会被访问。
也可以将其他内容放入ValueStack中,通常通过push标签来把期望的对象放入ValueStack上。
2 Struts2 标签概要
Struts2的标签可以分为4种类别:数据标签(data tag)、流程控制标签(control-flow tag)、UI标签(UI tag)和其他标签(miscellaneous tag)。
2.1 Struts2标签API语法
1. JSP语法
Struts 2标签的JSP版本与其他JSP标签一样。
<s:property value = "name" />
标签声明:
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
2. Velocity语法
#sproperty( "value=name" )
#sform ("action=Register")
#stextfield ("label=Username" "name=username")
#spassword ( "label=Password" "name=password")
#stextfield ( "label=Enter a name" "name=portfolioName")
#ssubmit ("value=Submit")
#end
3. FreeMarker语法
<@s.property value="name"/>
2.2 使用OGNL设置标签属性
向标签的属性设置值时有两点内容必须理解。最基本的问题是,这个属性期望一个字符串字面值,还是一个指向ValueStack上有具体类型的值的OGNL表达式。
1.字符串和非字符串类型的属性
如果一个属性的类型是String,那么在JSP或者Velocity页面中,写入属性的值会被作为字符串字面值解析。如果属性不是String类型,那么就会被当做OGNL表达式。
nonExistingProperty on the ValueStack = <s:property value="nonExistingProperty" />
nonExistingProperty on the ValueStack = <s:property value="nonExistingProperty" default="doesNotExist" />
第一个标签由于没有使用#操作符指明ActionContext中的特定对象,所以会在ValueStack上查找。如果nonExistingProperty属性在ValueStack上不存在,那么一个空值会转换为空字符串。
第二个标签仍然尝试从ValueStack上取出nonExistingProperty指向的属性,这个属性为空。然而该标签指定了default属性,在value指定的属性不存在的情况下会返回default属性指定的内容。由于default属性的作用是提供一个用来在页面显示的默认值,它是String类型。
使用%{expression}的语法可以强制让字符串属性当做OGNL表达式解析。
2. 强制使用OGNL解析
Struts 2 OGNL语法 | JSTL 表达式语法 |
---|---|
%{expression} | ${expression} |
3 数据标签
数据标签能从ValueStack上去的数据,或者将变量、对象放在ValueStack上。
3.1 property标签
属性 | 是否必须 | 默认值 | 类型 | 描述 |
---|---|---|---|---|
value | 否 | 栈最顶端 | Object | 被显示的值 |
default | 否 | 空 | String | 值为空时使用的默认值 |
escape | 否 | True | Boolean | 是否转义HTML |
<body>
<h4>Property Tag</h4>
The current user is <s:property value="user.username"/>.
</body>
3.2 set标签
设置set意味着给一个属性指定一个别名。使用后,该属性变为ActionContext中的一个命名对象。
属性 | 是否必须 | 类型 | 描述 |
---|---|---|---|
name | 是 | String | 引用名 |
scope | 否 | String | application、session、request、page或者action、默认值为action |
value | 否 | Object | 设置值的表达式 |
<s:set name="username" value="user.username"/>
Your username is <s:property value="#username"/>.
3.3 push标签
set标签允许你创建指向值的新引用,而push标签允许你将属性放到ValueStack上。
属性 | 是否必须 | 类型 | 描述 |
---|---|---|---|
value | 是 | Object | 放到ValueStack中的值 |
<s:push value="user">
<h2>This is the "<s:property value="portfolioName"/>"portfolio</h2>
It was created by none other than <s:property value="firstName"/> <s:property value="lastName"/>.
</s:push>
3.4 bean标签
bean标签像是set标签和push标签的混合标签。主要的不同是不需要使用一个已存在的对象。可以创建一个对象的实例,把它放到ValueStack上或者设置为ActionContext的顶级引用。
<s:bean name="org.apache.struts2.util.Counter" id="counter">
<s:param name="last" value="100"/>
</s:bean>
<s:iterator value="#counter">
<!-- Remember, the struts2 property tag defaults to the
top of the stack if a 'value' attribute is not set. -->
<li><s:property/></li>
</s:iterator>
该例子将bean创建在ActionContext中。
下面这个例子是将bean创建到ValueStack上而不是存储在ActionContext上。
<s:bean name="com.alan.web.uitags.utils.JokeBean" >
<s:param name="jokeType">adult</s:param>
<s:property value="startAJoke()"/>
</s:bean>
属性 | 是否必须 | 类型 | 描述 |
---|---|---|---|
name | 是 | String | 被创建bean的包名和类名 |
var | 否 | String | 在结束标签作用域之外引用这个bean时使用的变量名 |
3.5 action标签
action标签允许我们从当前的视图层调用其他的动作。
属性 | 是否必须 | 类型 | 描述 |
---|---|---|---|
name | 是 | String | 动作名 |
namespace | 否 | String | 动作的命名空间,默认使用当前页面的命名空间 |
var | 否 | String | 在页面后续代码中使用的动作对象的引用名 |
executeResult | 否 | Boolean | 设置为true时排除动作的结果(默认为false) |
flush | 否 | Boolean | 设置为true时会在action标签的结尾会刷新写出缓冲(默认true) |
ignoreContextParams | 否 | Boolean | 调用时不包含请求参数(默认为false) |
包含辅助动作的结果示例:
<body>
<h3>Action Tag</h3>
<hr/>
<h4>This line is from the ActionTag action's result.</h4>
<s:action name="TargetAction" executeResult="true"/>
<hr/>
</body>
如果想把动作的结果包含到当前页面,那么其他动作的结果应该是一个HTML片段。
4 控制标签
4.1 iterator标签
iterator标签可以非常容易地遍历集合对象。它可以遍历任何的Collection、Map、Enumeration、Iterator或者数组。
这个标签也支持在ActionContext中定义一个保存遍历状态的变量。
属性 | 是否必须 | 类型 | 描述 |
---|---|---|---|
value | 是 | Object | 被遍历的对象 |
status | 否 | String | 如果指定,IteratorStatus对象会使用这个属性指定的名字被放到ActionContext中 |
<s:iterator value="users" status="itStatus">
<li>
<s:property value="#itStatus.count" />
<s:property value="portfolioName"/>
</li>
</s:iterator>
iterator标签通过指定的status属性声明了IteratorStatus对象。而itStatus是保存在ActionContext中的,所以需要用#来获取。
IteratorStatus可获得的信息:
方法名 | 返回值 |
---|---|
getCount | int |
getIndex | int |
isEven | boolean |
isFirst | boolean |
isLast | boolean |
isOdd | boolean |
modulus(int operand) | int |
4.2 if和else标签
属性 | 是否必须 | 类型 | 描述 |
---|---|---|---|
test | 是 | Boolean | 被求值和测试的布尔表达式 |
<body>
<h4>If/Else Tags</h4>
<s:if test="user.age > 35">This user is too old.</s:if>
<s:elseif test="user.age < 35">This user is too young</s:elseif>
<s:else>This user is just right</s:else>
</body>
5 其他标签
有用但不容易分类的标签,include标签、URL标签、i18n标签和text标签。
5.1 include标签
属性 | 是否必须 | 类型 | 描述 |
---|---|---|---|
value | 是 | String | 页面、动作、Servlet以及其他可以被引用的URL名字 |
如JSP的<jsp:include>,Struts2 提供了个更高特性的include标签。
和action标签不同,include标签可以引用任何的Servlet资源。
使用include标签时,需要牢记正在直接包含一个JSP、Servlet或者其他的Web资源。包含其他Web资源的语义来源于Servlet API。
5.2 URL标签
属性 | 是否必须 | 类型 | 描述 |
---|---|---|---|
value | 否 | String | 基础URL,默认为呈现当前页面的URL |
action | 否 | String | 生成的URL指向的动作名,使用声明式架构中配置的动作名 |
var | 否 | String | 如果指定,URL不会被写出,而会存储在ActionContext留待后用 |
includeParams | 否 | String | 从all、get、none中选择参数,默认为get |
includeContext | 否 | Boolean | 默认true,生成的URL会以Context为前缀。 |
encode | 否 | Boolean | 如果用户浏览器不支持Cookie,会将Session ID追加到生成的URL中 |
scheme | 否 | String | 指定协议,默认为当前协议(HTTP/HTTPS) |
<h4>Url Tag</h4>
<hr/>
<h5>Url Tag Direct Usage</h5>
URL = <s:url value="IteratorTag.action"/><br/>
<a href='<s:url value="IteratorTag.action" />'> Click Me </a>
<hr/>
输出如下:
URL = IteratorTag.action
<a href='IteratorTag.action'> Click Me </a>
<h5>Url Tag Stashed in ActionContext</h5>
URL = <s:url action="IteratorTag" id="myUrl">
<s:param name="id" value="2"/>
</s:url>
<a href='<s:property value="#myUrl" />'> Click Me </a>
<hr/>
输出如下:
URL =
<a href='/manningHelloWorld/chapterSix/IteratorTag.action?id=2'>
Click Me
</a>
可以使用includeParams属性来指定参数是否传递到新的URL中。get的意思是只有URL中的查询字符串会传递到生成的URL中。post为表单。
5.3 i18n和text标签
i18n用来设置国际化。
text标签用来显示与具体语言相关的文本,这个标签从由框架的国际化机制公开的ResourceBundle中取得消息值。
text标签属性:
属性 | 是否必须 | 类型 | 描述 |
---|---|---|---|
name | 是 | String | 在ResourceBundle中查找用的关键字 |
var | 否 | String | 如果找到,文本会使用这个名字保存在ActionContext |
可以命名一个特别的ReourceBundle来解析text标签。如果想手动指定应该使用的ResourceBundle,可使用i18n标签。
i18n标签属性:
属性 | 是否必须 | 类型 | 描述 |
---|---|---|---|
name | 是 | String | ResourceBundle的名字 |
<s:i18n name="manning.chapterSix.myResourceBundle_tr">
<!-- The text tag can be used to output a resource inline or, if the 'id' attribute is used, the returned resource value will be stored in the action context for later use.-->
In <s:text name="language"/>,
<s:text name="girl" id="foreignWord"/>
</s:i18n>
"<s:property value="#foreignWord"/>" means girl.
i18n标签指定了一个需要使用的资源包。这个资源包仅在内部使用。
5.4 param标签
param是配置name和value的标签。
6 使用JSTL和其他文本标签
可以支持
7 OGNL表达式语言的基础知识
7.1 Struts 2中常用的表达式语言特性
1. 引用bean属性
OGNL表达式语言就是所谓的属性链 (chain of properties),如:
person.father.father.firstName
2.设置或取得
当数据设置时:
<s:form action="Register">
<s:textfield name="username" label="Username"/>
<s:password name="password" label="Password"/>
<s:textfield name="portfolioName" label="Enter a portfolio name."/>
<s:submit/>
</s:form>
比如这个例子,每一个输入字段的name属性都是一个OGNL表达式。这些表达式指向了OGNL跟对象公开的属性,而根对象就是ValueStack。
当情况开始复杂,可考虑深层表达式:
user.portfolio.name
当框架发现属性类有null属性时,他会尝试创建一个合适类型的新实例,并且将这个实例设置到对应的属性上。但是这个属性类型必须满足JavaBean规范的类,并且提供一个没有参数的构造方法。
当数据离开框架时,我们也使用OGNL表达式。
<h5>Congratulations! You have created </h5>
<h3>The <s:property value="portfolioName" /> Portfolio</h3>
3.使用Collection类型,数组与List
引用数组或者List属性元素的OGNL表达式语言语法:
Java代码 | OGNL表达式 |
---|---|
list.get(0) | list[0] |
array[0] | array[0] |
((User)list.get(0)).getName() | list[0].name |
array.length | array.lenght |
list.size() | list.size |
list.isEmpty() | list.isEmpty |
OGNL中动态创建List:
{1,2,3}
可见使用OGNL来表达数组或者list非常的直观与易用。
4.使用Map
Java代码 | OGNL表达式 |
---|---|
map.get(“foo”) | map[‘foo’] |
map.get(new Integer(1)) | map[1] |
User user = (User)map.get(“userA”); return user.getName() | map[‘userA’].name |
map.size() | map.size |
map.isEmpty() | map.isEmpty |
map.get(“foo”) | map.foo |
最后一行是一种特殊用法,如果key是String类型,则可直接使用.keystr。
OGNL动态创建Map
#{“foo”:”bar”,”baz”:”whazzit”}
#{1:”one”,2:”two”,3:”three”}
如果想提供true/false但现实为Yes/No,则可以使用#{true:’yes’,false:’No’}。
5.过滤(filtering)和投影(projecting)集合
过滤语法: collectionName.{? expression}
投影语法: collectionName.{ expression}
常用:
OGNL表达式 | 描述 |
---|---|
users.{? #this.age > 30} | 过滤,返回一个30岁以上用户的新集合 |
users.{username} | 投影,返回一个用户名字符串的新集合 |
user.{firstName + ’ ’ + lastName} | 投影,返回每一个用户全名字符串的新集合 |
users.{?#this.age > 30}.{username} | 先过滤,后投影,返回年龄在30岁以上的用户名字符串的新集合 |
7.2 高级特性
1.字面值和操作符
字面值类型 | 示例 |
---|---|
Char | ‘a’ |
String | ‘hello’、”hello” |
Boolean | true、false |
int | 123 |
double | 123.5 |
BigDecimal | 123b |
BigInteger | 123h |
操作符:
操作符 | 示例 |
---|---|
add(+) | 2 + 4 ‘hello’ + ‘world’ |
subtract(-) | 5 - 3 |
multiply(*) | 8 * 2 |
divide(/) | 9 / 3 |
modulus(%) | 9 % 3 |
increment(++) | ++foo,foo++ |
decrement(–) | bar–,–bar |
equality(==) | foo == bar |
less than(<) | 1 < 2 |
greater than(>) | 2 > 1 |
2. 调用方法
Java代码 | OGNL表达式 |
---|---|
utilityBean.makeRandomNumber() | makeRandomNumber() |
utilityBean.getRandomNumberSeed() | getRandomNumberSeed() randomNumberSeed |
假定utilityBean这个随机数生成器已经被压入了ValueStack
3.访问静态方法和字段
OGNL可通过指定完全限定的类名,以及ValueStack解析。
语法:@[fullClassName]@[property or methodCall]
示例:@manning.utils.Struts2PortfolioConstants@USER
@manning.utils.PortfolioUitiityBean@startImageWrapper( )
UI组件标签
内容:
- 使用UI组件生成HTML
- 构建表单以及其他内容
- 使用模板和主题增强效果
- 各式各样的组件
1 UI组件标签的作用
Struts2的UI组件不仅仅是表单元素,他还有以下功能:
- 生成HTML标记
- 绑定HTML表单字段和Java端属性
- 与框架提供的类型转换关联起来
- 与框架提供的验证关联起来
- 与框架提供的国际化功能关联起来
2 标签、模板和主题
3 UI组件标签
这些都不做详细描述,而作为一个概括的目录来展示这些组件。
3.1 通用属性
name、value、key、label、labelPosition、required、id、cssClass、cssStyle、disabled、tabindex、theme、templateDir、template
3.2 简单组件
1.head组件
<head>
<title>Portfolio Registration</title>
<s:head/>
</head>
<link rel="stylesheet" href=" . . . styles.css" type="text/css"/>
<script language="JavaScript" type="text/javascript" src=". . .dojo.js"/>
<script language="JavaScript" type="text/javascript" src="dojoRequire.js"/>
2.form组件
属性:action、namespace、method、target、enctype(上传文件时设置为multipart/form-data)、validate。
<s:form action="Login">
<s:textfield name="username" label="Username"/>
<s:password name="password" label="Password"/>
<s:submit/>
</s:form>
3.textfield组件
<s:form action="Login">
<s:textfield key="username"/>
<s:password name="password" label="Password"/>
<s:submit/>
</s:form>
4.password组件
属性:maxlength、readonly、size、showPassword
5.textarea组件
属性:cols、rows、readonly、wrap
6.checkbox组件
属性:fieldValue、value
3.3 基于集合的组件
1.select组件
下拉列表组件
<s:select name="user.name" list="{'Mike','Payal','Silas'}" />
2.radio组件
3.checkboxlist组件
4.预填充基于集合的组件
<s:form action="SelectPortfolio" >
<s:radio name="username" list='users' value="defaultUsername"
listKey="username" listValue="username" label="Select an artist" />
<s:submit value="Browse"/>
</s:form>
3.4 额外的组件
1.label组件
<s:label name="username" label="Username" />
这个组件很像一个只读的textfield组件
2.hidden组件
<s:hidden name="username" />
3.doubleselect组件
<h4>Select a portfolio to view.</h4>
<s:form action="ViewPortfolio">
<s:doubleselect name="username" list='users' listKey="username" listValue="username" doubleName="portfolioName"
doubleList="portfolios" doubleListValue="name" />
<s:submit value="View"/>
</s:form>
8 结果
内容
- 使用结果
- 分发和重定向请求
- 构建自定义结果
- 使用Velocity或者FreeMarker结果
8.1 动作之后
<action name="PortfolioHomePage" class=". . . PortfolioHomePage">
<result>/chapterEight/PortfolioHomePage.jsp</result>
</action>
8.1.1 构建Struts 2 Ajax应用程序
传统的Web应用程序:
响应的内容是一个完整的HTML页面,客户浏览器使用这个完整的页面呈现了整个窗口。
Ajax应用程序:
他只返回数据,当Ajax客户接受响应时,它不会让浏览器重新呈现整个HTML页面,而是仔细检查序列化为XML或者JSON的数据,对这些数据在当前浏览器页面上受影响的区域进行有目的的更新。
8.1.2 实现JSON结果类型
JSON(JavaScript Object Notation)提供了一种简洁的、基于文本的数据对象序列化的方式。JSON是Web应用程序服务器和Ajax客户之间数据通信的一种非常简洁和轻量级的方式。
1.Ajax例子
https://github.com/xu509/Alan/blob/master/src/main/webapp/page/result/ajaxUserBrowser.jsp
https://github.com/xu509/Alan/blob/master/src/main/webapp/page/result/ajaxUserBrowser.js
<action name="AjaxUserBrowser" class="com.alan.web.result.AjaxUserBrowser">
<result>/page/result/ajaxUserBrowser.jsp</result>
</action>
<action name="AjaxRetrieveUser" class="com.alan.web.result.RetrieveUser">
<result type="customJSON">artist</result>
</action>
2 常用的结果类型
结果类型 | 用例 | 参数 |
---|---|---|
dispatcher | JSP,其他web application资源,如Servlet | location、parse |
redirect | 告诉浏览器重定向到另一个URL | location、parse |
redirectAction | 告诉浏览器重定向到另一个Struts动作 | actionName、namespace,额外的参数会变成查询字符串的参数 |
2.1 RequestDispatcher,也叫做dispatcher
所有的示例代码都使用了这个类型,这是默认类型。
1.DispatcherResult的Servlet核心
DispatcherResut结果类型的核心是javax.servlet.RequestDispatcher。RequestDispatcher公开了两个方法来执行转交给其他的Servlet:include( )和forward( )。
2. 普通工作流,使用forward()分发
3. 迁移到其他servlet
4.通过include( )方法分发
5.建立RequestDispatcher结果类型
<action name="SelectPortfolio" class="manning.chapterSeven.SelectPortfolio">
<result type="dispatcher" >
<param name="location">/chapterSeven/SelectPortfolio.jsp</param>
<param name="parse">true</param>
</result>
</action>
2.2 ServletRedirectResult,也叫做redirect
1.建立重定向的结果类型
<action name="SendUserToSearchEngineAction" class="myActionClass">
<result type='redirect'>http://www.google.com</result>
</action>
2.嵌入OGNL表达式创建动态的位置
<action name="SendUserToSearchEngineAction" class="myActionClass">
<result type='redirect' >
http://www.google.com/?myParam=${defaultUsername}
</result>
</action>
2.3 ServletActionRedirectResult
redirectAction结果与简单的redirect结果做相同的事情。
<action name="Login" class="manning.chapterSeven.Login">
<result type="redirect">
/chapterSeven/secure/AdminPortfolio.action
</result>
<result name="input">/chapterSeven/Login.jsp</result>
</action>
<action name="Login" class="manning.chapterEight.Login">
<result type="redirectAction">
<param name="actionName">AdminPortfolio</param>
<param name="namespace">/chapterEight/secure</param>
</result>
<result name="input">/chapterEight/Login.jsp</result>
</action>
redirectAction能支持多种不同的URL模式的变更。
4 全局结果
<global-results>
<result name="error">/chapterFour/Error.jsp</result>
</global-results>
集成Spring
1 配置
相应jar包
需要Spring插件, struts2-spring-plugin-2.x.x.jar。
maven配置:
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.0.11</version>
</dependency>
web.xml配置
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
applicationContext配置
该文件位于web-inf文件夹下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id = "loginService" class="com.template.service.impl.LoginServiceImpl"/>
<bean id="loginAction" class="com.template.web.LoginAction" scope="prototype">
<property name="loginService" ref="loginService"/>
</bean>
</beans>
<action name="Login" class="springManagedLoginAction">
<result type="redirectAction">
<param name="actionName">AdminPortfolio</param>
<param name="namespace">/chapterEight/secure</param>
</result>
<result name="input">/chapterEight/Login.jsp</result>
</action>
可用Spring管理Struts的动作、拦截器以及结果的创建。