一、OGNL表达式语言
Ognl Object Graphic Navigation Language(对象图导航语言),它是一种功能强大的表达式语言(Expression Language,简称为EL),通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。
它使用相同的表达式去存取对象的属性
Struts 2 默认的表达式语言是 OGNL,原因是它相对其它表达式语言具有下面几大优势:
1.支持对象方法调用,如xxx.doSomeSpecial();
2.支持类静态的方法调用和值访问,表达式的格式为@[类全名(包括包路径)]@[方法名 | 值名],
例如:
@java.lang.String@format(‘foo %s‘, ‘bar‘)或@tutorial.MyConstant@APP_NAME;
3.支持赋值操作和表达式串联,如price=100, discount=0.8, calculatePrice(price*discount),这个表达式会返回80;
4.访问OGNL上下文(OGNL context)和ActionContext;
5.操作集合对象。
6.可以直接new一个对象
OGNL 的应用场景
在标签中:
<s:property value="user.username" /> //这个 user.username 是个ognl表达式
配置文件中
<result type="redirect" > /main.jsp?name=${userName} </result> //这里的 ${userName} 是ognl表达式
OGNL
访问属性
访问方法
访问静态属性或方法
访问构造函数
访问数组
访问集合
lambda
二、OGNL 应用举例
1) 属性获取
<s:property value="userName"/> = ${userName} <s:property value="user.userId"/> //对象的某属性
多级属性
<s:property value="user.schoolInfo.schoolName" />
2) 调用action中的静态方法
<s:property value="@cat.action.UserAction@getCountryName()" /> /* 附: 必须要加入一个常量 <constant name="struts.ognl.allowStaticMethodAccess" value="true" /> 才可以在ognl中调用静态方法 可以在 /org/apache/struts2/default.properties 中,查看到所有常量的值 */
3) 调用JDK 中类的静态方法
<s:property value="@java.lang.Math@floor(50.99)"/>
4) 调用值栈中对象的普通方法
<s:property value="user.testUser()"/>
5) 调用普通类中的静态属性
<s:property value="@cat.bean.UserInfo@country"/>
6) 调用构造方法
<s:property value="new cat.bean.UserInfo(100,‘张三‘).userName"/>
本例要在UserInfo 类中 增加一个构造函数
public UserInfo(int id,String userName){ this.id=id; this.userName=userName; System.out.println("this is a dog"); }
7) 获取集合中的内容
假如值栈中有一个ArrayList,里面放的是 叫 userList ,里面的值是 "aaa","bbb","ccc"则可以: <s:property value="userList[0]"/> 得到 aaa
假如值栈中有一个 List<UserInfo>userList,则可以以数组的方式取数据 <s:property value="userList[0].userName"/>
8) 获取 map 中的值
<s:property value="userMap[‘aaa‘].userName"/> //aaa 是个key
集合的伪属性
OGNL 能调用集合的一些特殊属性,它些属性并不是javaBean 模式的
主要有以下几个
size, isEmpty,
iterator
keys,values
next hasNext
nextElement,hasMoreElements
获取 map 中所有的键<s:property value="userMap.keys"/>
取 list 的长度 <s:property value="userList.size"/>
三、ActionContext 和值栈
Strut2中 OGNL Context 实现者为 ActionContext 它的结构如下
--- ValueStack (值栈,是根对象)
--- parameters
--- request
--- session
--- application
--- attr
当 struts 接受一个请求的时候, 会迅速的创建 ActionContext, ValueStack, Action, 然后把 Action放到 ValueStack 中,Action 实例对象可以被 OGNL 访问。
注意:
在页面用OGNL设置值()这些访问的都是上下文中的这些对象的属性)
<s:property value="#request.requestKey"/> 注意要用# 不用是访问不到的
<s:property value="#session.sessionKey"/>
用el表达式也能达到相同的效果
${ requestKey}
${ sessionKey}
parameters 的应用场景
<result type="redirect">/b.jsp?name=1234455</result> //注意如果这里不是redirect 方式,是取不到的 <s:property value=#attr.name / >
四、串连多个Action的值栈
1 写两个Action, A,和 B, 里面分别各自放两个不同名和字段,放一个同名的字段
public class A { private String a_userName; private String a_password; private String xxx; public String execute(){ a_userName="张三"; a_password="sys123"; xxx="这是A里的xxx的值"; return "success"; } } public class B { private String b_goodsName; private String b_goodsNo; private String xxx; public String execute(){ b_goodsName="茶杯"; b_goodsNo="009"; xxx="这是b里xxx的值"; return "success"; }
<package name="packageOne" namespace="" extends="struts-default"> <action name="a" class="cat.action.A"> <result type="chain"> <param name="actionName">b</param> </result> </action> <action name="b" class="cat.action.B"> <result>/Main.jsp</result> </action> </package>
注意:当struts2接收一个请求的时候,它会迅速的创建一个 上下文对象 ActionContex,然后创建 ValueStack,然后创建。
action,再将 action放到ValueStack中
action 已经放到根对象中去了,我们要访问根对象中的属性,是不需要#号的
五、使用ognl操作集合
采用ognl表达式创建 List/Map集合对象
<s:set> 标签用于将某个值放入指定范围。它有以下两个属性
scope:指定变量被放置的范围,该属性可以接受application、session、request、 page或action。如果没有设置该属性,则默认放置在 OGNL Context 中。
value:赋给变量的值.如果没有设置该属性,则将 ValueStack栈项的值赋给变量。
//例一:操作 List集合 <s:set var="userList" value="{‘黄世仁‘,‘邓世昌‘,‘李刚‘,‘杨达才‘}" /> ---//例一 s:iterator 标签在迭代时有一个特点,会把当前迭代的对象放在值栈的栈顶 <s:iterator value="#userList"> <s:property /> <br> //对于 s:property 如果不指定value 则value取值栈顶的值 </s:iterator> ---//例二 <s:iterator value="#userList" id="user"> <s:property value="user" /> <br> //这里指定了 property的 value 就是 上边的 id ${user } //这里用El表达式也行 </s:iterator> --- //例三 (处理声明时使用了scope 属性的集合) <s:set var="userList" value="{‘黄世仁‘,‘邓世昌‘,‘李刚‘,‘杨达才‘}" scope="session" /> <s:iterator value="#session.userList" id="user" > <s:property value="user" /> <br> ${user } </s:iterator>
//例二:操作 Map 集合 //注意,如果map中key对应的value是数字型,则可以不用‘‘号 <s:set var="userMap" value="#{‘1号首长‘:‘江‘,‘二号首长‘:‘涛‘,‘三号首长‘:‘平‘ }" /> <s:iterator value="#userMap" > <s:property value="key"/>= <s:property value="value"/> <br> </s:iterator> 对于集合类型,OGNL表达式可以使用in和not in两个元素符号判断元素是否在某个集合中============= <s:if test="‘aaa‘ in {‘aaa‘,‘bbb‘}"> 在 </s:if> <s:else> 不在 </s:else> 如果集合是个变量也可以 <s:if test="‘黄世仁‘ in #userList"> 在 </s:if> <s:else> 不在 </s:else>
六、ognl表达式的投影功能
1 对项目添加数据库访问,然后在Action中,查出用户列表 userList 放在值栈里
2 UserManage.jsp 中
==全部取出
<s:iterator value="userList"> <s:property value="userId"/>=<s:property value="userName"/><br> </s:iterator>
==带投影的
?:获得所有符合逻辑的元素。
^:获得符合逻辑的第一个元素。
$:获得符合逻辑的最后一个元素。
<s:iterator value="userList.{?#this.id>200}"> <s:property value="id"/>=<s:property value="userName"/><br> </s:iterator>
==用jstl的
<c:forEach var="user" items="${userList}" >
${user.userId} | ${user.userName} <br>
</c:forEach>
七、struts 标签库概述
Struts2标签分类
(1)UI标签:(User Interface, 用户界面)标签,主要用于生成HTML元素标签,
UI标签又可分为表单标签非表单标签
(2)非UI标签,主要用于数据访问,逻辑控制等的标签。非UI标签可分为流程控制标签
(包括用于实现分支、循环等流程控制的标签)和数据访问标签(主要包括用户输出ValueStack中的值,完成国际化等功能的)
(3)ajax标签
==流程控制标签
if
elseif
else
append //合并多个集合到一个新集合里
generator //把字符串按某种标志进行切割成集合
iterator
merge //合并多个集合到一个新集合里
sort
subset
==数据访问标签
a
action
bean
date
debug
i18n
include
param
property
push
set
text
url
八、Struts2 数据访问标签
(一)<s:set> 标签 //设置值
<s:set var="dog" value="‘安倍晋三‘" /> 严重注意,dog 是要用单引号扩起来的, var 可以换写成name,但name被废弃了 //取值: <s:property value="#dog"/> //可以 <s:property value="dog"/> //可以 ${dog } //可以 <s:debug />
结过观察,发现如果不指定scope, 它是放在 Stack Context (其实就是context map, Acton Context,OGNL Context,) 中的<hr/>
<c:set var="num1" value="ooooooooo" scope="request" /> 这是jstl 标签
关于 scope :
The scopes available are as follows :-
application - the value will be set in application scope according to servlet spec. using the name as its key
session - the value will be set in session scope according to servlet spec. using the name as key
request - the value will be set in request scope according to servlet spec. using the name as key
page - the value will be set in page scope according to servlet sepc. using the name as key
action - the value will be set in the request scope and Struts‘ action context using the name as key ‘
关于 set 的 参数
Name Required Default Evaluated Type Description
id false false String Deprecated. Use ‘var‘ instead //Deprecated 这个单词是不选成,反对的意思
name false false String Deprecated. Use ‘var‘ instead
scope false action false String The scope in which to assign the variable. Can be application, session, request, page, or action.
value false false String The value that is assigned to the variable named name
var false false String Name used to reference the value pushed into the Value Stack
2.<s:property> 标签
用于输出表达式的值,如果指定value 属性,就输出value 对应的值,如果没有指定,它会输出ValueStack 栈顶的值
1,访问Action值栈中的普通属性:
<s:property value="attrName"/>
2,访问Action值栈中的对象属性(要有get set方法):
<s:property value="obj.attrName"/> <s:property value="obj1.obj2.attrName"/>
3,访问值栈中对象属性的方法
<s:property value="obj.methodName()"/>
4,访问值栈中action的普通方法:
<s:property value="methodName()"/>
5,访问静态方法:
<s:property value="@com.softeem.LoginAction@methodName()"/>
6,访问静态属性:
配置属性文件,允许ognl访问静态方法struts.ognl.allow...=true
<s:property value="@com.softeem.LoginAction@attrName"/>
7,访问Math类的静态方法:
<s:property value="@@min(9,7)"/>
8,访问普通类的构造方法:
<s:property value="new com.softeem.User(2)"/>
9,访问集合:
①list集合对象
<s:property value="listName"/>
②list集合中的某个元素
<s:property value="listName[1]"/>
③list中某个属性的集合
<s:property value="listName.{field}"/>
④list中某个属性集合的特定值
<s:property value="listName.{field}[0]"/>
⑤访问set
<s:property value="setName"/>
⑥访问set中某个元素
<s:property value="setName[0]"/>
⑦访问map
<s:property value="mapName"/>
⑧根据key访问Map中的元素
<s:property value="mapName.username"/> <s:property value="mapName[‘username‘]"/> <s:property value="mapName[/"username/"]"/>
⑨访问map中所有的key
<s:property value="mapName.keys"/>
10,访问map中所有的values
<s:property value="mapName.values"/>
11,访问map的大小
<s:property value="mapName.size()"/>
12,投影
<s:property value="listName.{?#this.age==1}"/>
<s:property value="listName.{^#this.age>1}"/>
<s:property value="listName.{$#this.age==1}"/>
<s:property value="listName.{$#this.age==1}.{age}==null"/>
[]:<s:property value="[0]"/>值栈中的对象
default:可选属性,如果需要输出的属性值为null,则显示该属性指定的值
escape:可选属性,指定是否格式化HTML代码,不搭荐,应该用escapeHtml
value:可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值。
id:可选属性,指定该元素的标识 这个属性好象在新版本中已经没了
Name Required Default Evaluated Type Description
default false false String The default value to be used if value attribute is null
// escape false true false Boolean Deprecated. Use ‘escapeHtml‘. Whether to escape HTML
escapeCsv false false false Boolean Whether to escape CSV (useful to escape a value for a column)
escapeHtml false true false Boolean Whether to escape HTML
escapeJavaScript false false false Boolean Whether to escape Javascript
escapeXml false false false Boolean Whether to escape XML
value false <top of stack> false Object Value to be displayed
//例子: <s:set var="userName" value="‘struts标签真烂‘" /> 严重注意, 里面要有单引号 <s:property value="#userName" /> //正常该加 # <s:property value="userName" /> //不加也能取出来 <c:out value="${userName}"></c:out> //用jstl标签也能取
上边的功能完全可以用 jstl标签取代 如下:
<c:set var="userMsg" value="struts 标签库的设计者是白痴" /> ${userMsg} <c:out value="${cat_name}" default ="--------------------"/>
(二)<s:action> 标签
是用来调用Action的标签,在JSP中页面中,可以具体指定某一命名空间中的某一Action。而标签的主体用于显示及渲染Actionr的处理结果。
action标签有如下几个属性:
1、id: 可选,作为该action的引用ID //作废
2、name: 必填,调用action的名字
3、namespace: 可选,action所在的nqmespace
4、executeResult,可选,指定是否将action的处理结果页面包含到本页面。
5、ignoreContextParame: 可选,指定该页面的请求参数是否需要传入action.
6、var
7、rethrowException
8、flush
//这个userAction的名字,是给本页面中别的标签引用用的, <s:action name="listUser" var="page_userAction" executeResult="false"> 如果把executeResult换成true则会把result对应的视图传过来 <s:param name="paramAAA">调Action时传一个参数</s:param> 可以通过这种方法给被调用的action传参数,在action中可以用值栈的方式取到它 </s:action> <s:iterator value="#userAction.userList"> 可以这样取出被调用的action中的值,注意,这些值是放在 StackContext中的,所以要加# <s:property value="userName" /> </s:iterator> <c:forEach var="user" items="${page_userAction.userList}"> 使用jstl 标签,把传过来的值生成一组复选框 ${user.userName}: <input type="checkbox" value=‘${user.userId}‘ /> </c:forEach> <s:debug />
== <s:push> 把表达式的值放入valueStack 的栈顶
== <s:url> 标签
<s:url action="UserAction" namespace="/test">
<s:param name="userId" value="10"/>
<s:param name="userName" value="‘赵铁冷‘"/> 需要注意: 如果value是汉字,早要加单引号 ,如果是中文,会被url编码
</s:url>
//实例: 访问UserAction <a href=‘<s:url action="UserAction" namespace=""> //UserAction是配置在xml文件中的名字 <s:param name="userId" value="10"/> <s:param name="userName" value="‘赵铁冷‘"/> </s:url>‘ > 去Action里串串门 </a>
知识点回顾: 在UserAction中取出 userName
可以在UserAction 中定义一个 userName 字段,生成get和set方法,这是前面常用的 struts取请求参数的方法。硬生生的取得request对象,从requet中取得,这是以前常用的方法,如下:
HttpServletRequest request=ServletActionContext.getRequest(); String tempStr=request.getParameter("userName"); tempStr=new String(userName.getBytes("ISO8859-1"),"UTF-8"); String userName=URLDecoder.decode(tempStr,"UTF-8"); //其实在这个地方不decode也行 //打印 userName <s:set name="myurl" value="‘http://www.baidu.com‘"/> <s:url value="#myurl" /> <s:url value="%{#myurl}" /> //它的value默认接收的是字符串,而不是ognl表达式,如果想用表达式得转一下
九、Struts2 流程控制标签
==iterator标签
它的参数,还有 begin 和 end 从第几个元素开始,迭代几个 begin ="2" end="4",用于对集合进行迭代,这里的集合包含List、Set和数组。
<s:set name="list" value="{‘西瓜‘,‘哈密瓜‘,‘大傻瓜‘,‘半拉瓜‘}" /> <s:iterator value="#list" var="瓜名" status="st"> //这里的var 也可以用 id,但id 不推荐 <font color= <s:if test="#st.odd">red</s:if> <s:else>blue</s:else> > <s:property value="瓜名"/> count: <s:property value="#st.getCount()" /> index: <s:property value="#st.getIndex()" /> </font><br> </s:iterator>
== if else
<s:set var="广告名" value="‘脑白金‘" /> <s:if test="#广告名==‘脑白金‘"> //注意,这里不用 #可就不行了 脑残广告,傻子才买 </s:if> <s:elseif test="#广告名==‘梦酒‘"> 五粮醇风,古方秘制,钦誉中外,独占鳌头 </s:elseif> <s:else> 说不定是就锅王胡师傅 </s:else>
可以用jstl代替上边的功能:
<c:choose> <c:when test="${广告名==‘梦酒‘}"> 一看就想喝的梦酒</c:when> <c:when test="${广告名==‘脑白金‘}">让人想吐的脑白金</c:when> <c:otherwise>估计十有八九是锅王胡师傅</c:otherwise> </c:choose>
//对于上例,可以指定 scope <s:set name="广告名" value="‘脑白金‘" scope="request"/> 指定泛围为 request <s:if test="#request.广告名==‘脑白金‘"> 取的时候也要用 request. 脑残广告,傻子才买 </s:if> <s:elseif test="#request.广告名==‘梦酒‘"> 五粮醇风,古方秘制,钦誉中外,独占鳌头 </s:elseif> <s:else> 说不定是就锅王胡师傅 </s:else>
十、Struts2 UI标签
== 主题和模板
Struts2提供了三种主题,ajax, simple, css_xhtml,xhtml,它默认的是xhtml主题,开发时我们一般都选simple。因为Struts2所有的UI标签都是基于主题和模板的,主题和模板是Struts2所有UI标签的核心。模板是一个UI标签的外在表示形式,例如:当我们使用<s:select ... ... />标签时,Struts2就会根据对应select模板来生成一个有模板特色的下拉列表框。如果为所有的UI标签都提供了对应的模板,那么这系列的模板就形成了一个主题。
对于一个JSP页面里包含的UI标签而言,即可以直接设置该UI标签需要使用的模板,也可以设置该UI标签使用的主题。实际上对开发者而言,并不推荐直接设置模板属性,而是应该选择特定主题。设置主题的方法有以下几种:
1,通过设定特定UI标签上的theme属性来指定主题。
2,通过设定特定UI标签外围的Form标签的theme属性来指定主题。
3,通过取得page\request\session\application 会话范围内以theme为名称的属性来确定主题。
4,通过取得名为struts.ui.theme的常量(默认值是xhtml)来确定主题,该常量可以在struts.properties文件或者struts.xml文件中确定。如:
<constantname="struts.ui.theme" value="simple" />
==表单标签
1 <s:checkboxlist>
====如果集合为list
<s:checkboxlist name="list" list="{‘Java‘,‘.Net‘,‘RoR‘,‘PHP‘}" value="{‘Java‘,‘.Net‘}"/>
生成如下html代码:
<input type="checkbox" name="list" value="Java" checked="checked"/><label>Java</label> <input type="checkbox" name="list" value=".Net" checked="checked"/><label>.Net</label> <input type="checkbox" name="list" value="RoR"/><label>RoR</label> <input type="checkbox" name="list" value="PHP"/><label>PHP</label>
它默认会生成一些主题相关的tr td等,如果不想要,可以在struts.xml中配置 <constant name="struts.ui.theme" value="simple" />
====如果集合为MAP
<s:checkboxlist name="map" list="#{1:‘瑜珈用品‘,2:‘户外用品‘,3:‘球类‘,4:‘自行车‘}" listKey="key" listValue="value" value="{1,2,3}"/>
生成如下html代码:
<input type="checkbox" name="map" value="1" checked="checked"/><label>瑜珈用品</label> <input type="checkbox" name="map" value="2" checked="checked"/><label>户外用品</label> <input type="checkbox" name="map" value="3" checked="checked"/><label>球类</label> <input type="checkbox" name="map" value="4"/><label>自行车</label>
====如果集合是里放的是javaBean
<% Person person1 = new Person(1,"第一个"); Person person2 = new Person(2,"第二个"); List<Person> list = new ArrayList<Person>(); list.add(person1); list.add(person2); request.setAttribute("persons",list); %> <s:checkboxlist name="beans" list="#request.persons" listKey="personid" listValue="name"/>
Personid和name为Person的属性
生成如下html代码:
<input type="checkbox" name=“beans" value="1"/><label>第一个</label> <input type="checkbox" name=“beans" value="2"/><label>第二个</label>
例子:在 UserManage.jsp 中 (因为前面已经做过列出用户的例子,所以现在可以直接取内容)
<s:checkboxlist name="beans" list="userList" listKey="id" listValue="userName"/>然后 访问 http://localhost:8080/OGNL/UserAction!getAllUser
2 <s:radio>
该标签的使用和checkboxlist复选框相同。如果集合里存放的是javabean(personid和name为Person的属性)
<s:checkboxlist name="beans" list="#request.persons" listKey="personid" listValue="name"/>
生成如下html代码:
<input type="radio" name="beans" id="beans1" value="1"/><label>第一个</label> <input type="radio" name="beans" id="beans2" value="2"/><label>第二个</label>
如果集合为MAP
<s:radio name="map" list="#{1:‘瑜珈用品‘,2:‘户外用品‘,3:‘球类‘,4:‘自行车‘}" listKey="key" listValue="value“ value="1"/>
生成如下html代码:
<input type="radio" name="map" id="map1" value="1"/><label for="map1">瑜珈用品</label> <input type="radio" name="map" id="map2" value="2"/><label for="map2">户外用品</label> <input type="radio" name="map" id="map3" value="3"/><label for="map3">球类</label> <input type="radio" name="map" id="map4" value="4"/><label for="map4">自行车</label>
如果集合为list
<s:radio name="list" list="{‘Java‘,‘.Net‘}" value="‘Java‘"/>
生成如下html代码:
<input type="radio" name="list" checked="checked" value="Java"/><label>Java</label> <input type="radio" name="list" value=".Net"/><label>.Net</label>
十一、<s:token > 用来防止表单重复提交
例子:重复提交的例子
1.
private UserInfo userInfo; public String add(){ UserDao dao=new UserDao(); dao.addUser(this.userInfo); return "success"; }
2 struts.xml中 配置Action
<action name="UserAction_Add" class="cat.action.UserAction" method="add">
<result name="success">/Succ.jsp</result>
</action>
3 UserAdd.jsp中
<s:form action="UserAction_Add" namespace="" method="post"> 姓名:<s:textfield name="userInfo.userName" /> 账号:<s:textfield name="userInfo.userId" /> 密码:<s:password name="userInfo.password" /> 备注:<s:textarea name="userInfo.note" /> <s:submit value="提交" /> </s:form>
4 进行提交,看刷新后的重复提交的结果
使用 tokey 标签防止重复提交,用法如下:
1 在表单中加入<s:token />
...
<s:token/> //加入这个tokey
....
2 在XML中
<action name="UserAction_Add" class="pinkcat.action.UserAction" method="add"> <interceptor-ref name="defaultStack"/> //注意,默认的拦截器栈一定要加上 <interceptor-ref name="token"/> <result name="invalid.token">/UserAdd.jsp</result> //设置用户如果重复提交,则返回添加页 <result name="success">/Succ.jsp</result>
3 在UserAction 中 让其继承自 extends ActionSupport
十一、拦截器
Interceptor
和过滤器很象,但它只捕获 Action。在Struts2 中 许多Action通常都有一些共同需要关心的问题,比如有一些Action 它们都需要对页面上的输入进行校验,有一些Action需要对上传的文件进行一下预处理,还有一些Action可能需要防止表单的重复提交 (double submit) 还有很多Action需要在页面显示之前将下拉列表和其它一些控件事先装好值,通过拦截器策略,Struts2 框架使得共享这些问题更容易。
拦截器能在 Action 被调用之前和被调用之后执行一些 "代码",Struts2 框架的大部分功能都是通过拦截器来实现的,如防止重复提交,类型转换,对象封装,校验,文件上传,页面装载等等。每一个拦截器都是独立装载的(pluggable) ,可以根据实际的需要为每一个Action 配置需要用的拦截器. 例如: 一个Action 需要用到类型转换器 和 文件上传,那么我们可以给它设置两个拦截器。
拦截器是Struts2 框架的核心,包括解析请求参数,将请求参数赋值给Action属性,执行数据校验,文件上传等工作都是用它实现的Struts2设计的灵巧性,更多的得益与拦截器的设计.当需要进行扩展时,只需要提供对应的拦截器,并将它配置在Struts2容器中即可,反之可以摘掉 (AOP) 面向切面。
十二、struts2 自带的拦截器
Struts2 内建了大量拦截器,它们 以 name-class 对的形式配置在 struts-defalut.xml 文件中
alias: 实现在不同请示中相似参数别外的转换
autowiring 自动装配的拦截器,用于和Spring 进行整合
chain 构建一个Action链,使当前的Action可以访问前一个Action的属性 一般和 <result type="cahin"> 一起使用
conversionError 处理类型转换错误 它负责将类型转换错误从ActionContext 中取出,并转换成Action 的FieldError 错误
createSession 负责创建一个HttpSession对象
debugging 当使用Struts2 开发模式时,它会提交更多的调试信息
execAndWait 后台执行Action,负责将等待画面发送给用户
execption 处理异常,将异常映射为结果
fileupload 文件上传,解析表单域中的内容
i18n 国际化,负责把所选语言,区域放入用户Session中
ogger 日志记录,主要是输出Action 的名字
model-driven 一个用于模型驱动的拦截器, 当某个Action 类实现了 ModeDriven接口时,它负责把getModel() 方法的结果堆入ValueStack中
scope-model-driven
params 最基本的一个拦截器,负责解析Http请示中的参数,并将参数值设置成Action对应的属性值
prepare 如果Action实现了Prepareable 接口,会调用访栏截器中的prepare()方法
static - params 将XML中<action>标签下的<param>标签中的参数传入 Action
scope 范围转换 可以将Action 状态信息保存到 HttpSession 或 ServletContext 范围内
servlet-config 如果某个Action 需要直接访问 Servlet Api ,则通过它实现(尽量避免在Action中直接访问Servlet Api,高耦合)
roles JAAS java权限和认证服务 拦截器,有权才能调用该拦截器拦截的Action
timer 输出Action执行时间 分析Action性能的瓶颈时有用
token 阻止重复提交
token -session 和上面相似,只是把token保存在HttpSesison中
validation 通过执行在xxxAction-validation.xml中定义的校验器.从而完成数据校验
workflow 负再调用 Action类中的 validation 方法,如失败则返回 Input视图
十三、自定义拦截器
自定义拦截器有三种方式
1) 实现 Interceptor 接口
2) 继承自 AbstractInterceptor
3) 继承自 MethodFilterInterceptor
例一 计算一个Action的执行时间的拦截器,继承自 AbstractInterceptor方式实现
1) 定义一个拦截器类
public class MyInterceptor extends AbstractInterceptor { //这个String 代表执行完Action后的返回值 public String intercept(ActionInvocation invocation) throws Exception { System.out.println("拦截开始"); long begintime=System.currentTimeMillis(); String result=invocation.invoke(); //执行A ction中的对应的方法 long endtime=System.currentTimeMillis(); System.out.println("该action 共执行了:"+(endtime-begintime)+" ms"); System.out.println("拦截结束"); return result; } }
关于 ActionInvocation
ActionInvocation就是Action的调用者。ActionInvocation在Action的执行过程中,负责Interceptor、Action和Result等一系列元素的调度。Interceptor通过ActionInvocation可以完全的改变 Action行为:不让它执行、改变返回值、甚至可以细颗粒的操作Action的方法。它有getActionProxy(),getResultCode();getInvocationContext();getAction() 等方法。比如 可以通过 getAction 拿到Action对象,并执行里面的方法:
MoreMethodAction M=(MoreMethodAction)invocation.getAction();
M.test();
或者通过它拿到 ActionContext 对象
ActionContext act=invocation.getInvocationContext();
2) 在配置文件中配置
<package name="packageOne" extends="struts-default" > <interceptors> <interceptor name="myInte_AAA" class="cat.interceptor.MyInterceptor" /> </interceptors> <action name="userAction_*" class="cat.action.UesrAction" method="{1}" > <result name="success">/main.jsp</result> <interceptor-ref name="myInte_AAA" /> <interceptor-ref name="defaultStack" /> //如果引入了自定义的拦截器,系统就不再提供默认的拦截器栈了,所以要自己手功引入 </action> </package>
3) action
public class UesrAction { public String add(){ System.out.println("add 方法被调用了"); return "success"; } public String del(){ System.out.println("del 方法被调用了"); return "success"; } public String update(){ System.out.println("update 方法被调用了"); return "success"; } public String search(){ System.out.println("search 方法被调用了"); return "success"; } }
例二 灵活的限定拦截哪些方法,不拦截哪些方法= //通过继承 MethodFilterInterceptor 方式实现
1) 拦截器
public class MyInterceptor2 extends MethodFilterInterceptor{ protected String doIntercept(ActionInvocation invocation) throws Exception { System.out.println("MethodFilterInterceptor 启动"); String result=invocation.invoke(); System.out.println("MethodFilterInterceptor 结束"); return result; } }
2) 配置文件
<package name="packageOne" extends="struts-default" > <interceptors> <interceptor name="myInte_AAA" class="cat.interceptor.MyInterceptor" /> <interceptor name="myInte_BBB" class="cat.interceptor.MyInterceptor2" > //这是针对 MyInterceptor2 的 <param name="excludeMethods">search,update,del</param> //这里的面的方法,不会被拦截 </interceptor> </interceptors> <action name="userAction_*" class="cat.action.UesrAction" method="{1}" > <result name="success">/main.jsp</result> <interceptor-ref name="myInte_AAA" /> <interceptor-ref name="myInte_BBB" /> //引用声明的拦截器 <interceptor-ref name="defaultStack" /> </action> </package>
3) action 同上例一样,略
访问 userAction_add,userAction_del 等方法,可以发现, <param name="excludeMethods">search,update,del</param> 中声明的这几个方法没有被拦截。
附: 使用拦截器栈
<package name="packageOne" extends="struts-default" > <interceptors> <interceptor name="myInte_AAA" class="cat.interceptor.MyInterceptor" /> <interceptor name="myInte_BBB" class="cat.interceptor.MyInterceptor2" > <param name="excludeMethods">search,update,del</param> </interceptor> <interceptor-stack name="myStack"> //将自己声明的两个拦截器和系统的 defaultStack 组成一个拦截器栈 <interceptor-ref name="myInte_BBB" /> <interceptor-ref name="myInte_AAA" /> <interceptor-ref name="defaultStack" /> </interceptor-stack> </interceptors> <action name="userAction_*" class="cat.action.UesrAction" method="{1}" > <result name="success">/main.jsp</result> <interceptor-ref name="myStack" /> //引用拦截器栈 </action> </package>
例三 系统拦截器的调用,登录延时提醒ExecuteAndWaitInterceptor 很适合在后台长时间运行的action时,它可以可为用户一个友好的等待界面,例如进度条。
不在默认的 defaultStack 中
1)
<action name="loginAction" class="cat.action.UesrAction" method="login">
<result name="success">/main.jsp</result>
<result name="wait" >/wait.jsp</result>
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="execAndWait">
<param name="delay">2000</param>
</interceptor-ref>
</action>
2) wait.jsp 中
<meta http-equiv="refresh" content="1;URL=loginAction" > //或 response.setHeader("refresh", "3;URL=loginAction");
3) 在loginAction 设置 Thread.sleep(4000)
本例说明:
当调用 loginAction 的时候,程序会根据 配置文件中 delay 属性( 2000 )等待Action先执行 2000 毫秒, 但由于Action 中 sleep(4000), 所以它没执行完,这时拦截器就把请求发到 wait 视图上去了,wait 视图中每隔一秒 访问一下 loginAction,但这时loginAction对应的那个 Action还在沉睡中,所以访问也是无效的,(这些请求都被忽略了?) 直到它睡醒, 这时刷过去的请求就直接到result上了。
例四 Session 权限拦截器
public class SessionInterceptor extends MethodFilterInterceptor{ @Override protected String doIntercept(ActionInvocation invocation) throws Exception { //方式一 (取到sesssion中的内容) /*ActionContext ctx=ActionContext.getContext(); ctx.getSession()*/ //方式二 //ServletActionContext.getRequest().getSession(); //方式三 UserInfo user=(UserInfo)invocation.getInvocationContext().getSession().get("userInfo"); if(user!=null){ return invocation.invoke(); } else{ System.out.println("没有登录"); ActionContext.getContext().put("msg","您还没有登录"); return "no_login"; } } } <interceptor name="session_Inte" class="cat.interceptor.SessionInterceptor" > <param name="excludeMethods">login</param> </interceptor> ... <action name="sessionAction_*" class="cat.action.UesrAction" method="{1}" > <result name="success">/main.jsp</result> <interceptor-ref name="session_Inte" /> <interceptor-ref name="defaultStack" /> </action>
十四、在struts2中使用Ajax
(通过 stream 类型的 result 实现)
public class AjaxAction { private String userName; private String result; ... get set 方法 public String checkUser(){ if("admin".equals(userName)){ result="用户名已经被别人使用"; return "success"; } else{ result="用户名可用"; return "success"; } } //把字符串转成 ByteArrayInputStream public InputStream getInputStream() throws UnsupportedEncodingException { return new ByteArrayInputStream(result.getBytes("utf-8")); }
页面: ajax_test.jsp
$(function() { $("#btn1").click(function(){ $.ajax({ url:"ajaxAction_checkUser", data:{userName:"admin"}, type:"post", success:function(data){ $("#divResult").html(data); } }); }); }); //或 $("#div2").load("ajaxAction_checkUser",{userName:"admin"}); //这样写更简单 <package name="packageTwo" extends="struts-default" > <action name="ajaxAction_*" class="cat.action.AjaxAction" method="{1}"> <result type="stream" > <param name="contentType">text/html</param> <param name="inputName">InputStream</param> </result> </action> </package>
十五、在struts2中使用Ajax
(通过 json 插件实现)
//页面 $("#btn2").click(function(){ $.ajax({ url:"ajaxAction2_getAllUser", type:"post", dataType:"json", success:function(userList){ $.each(userList,function(key,user){ var trStr="<tr><td>"+user.id+"</td><td>"+user.userName+"</td><td>"+user.password+"</td></tr>"; $("#table1").append(trStr); }); } }); //Action public class AjaxAction2 { private String userName; private String result; List<UserInfo> userList; public String getAllUser(){ userList=new UserDao().getAllUser(); return "success"; } //配置文件 <package name="packageThree" extends="json-default" > <action name="ajaxAction2_*" class="cat.action.AjaxAction2" method="{1}"> <result name="success" type="json" > <param name="root">userList</param> </action> </package>
附加说明:
如果在上面的 action 中不指定 <param name="root">userList</param> 这个选项,则它会把整个action 对象做为json 数据返回,如果这样,前而取数据的方式如下:
$("#btn2").click(function(){ $.ajax({ url:"ajaxAction2_getAllUser", type:"post", dataType:"json", success:function(action){ var userList=action.userList; //因为返回的是整个action 所以这里要这样取出 console.info(userList); $.each(userList,function(key,user){ var trStr="<tr><td>"+user.id+"</td><td>"+user.userName+"</td><td>"+user.password+"</td></tr>"; $("#table1").append(trStr); }); } });