您的位置:首页 > 产品设计 > UI/UE

【JavaWeb-18】ActionContext存取数据、ValueStack存取值、EL新查找顺序、iterator、OGNL投影、其他标签、UI主题、防重复提交

2016-10-07 17:07 791 查看
1、我们之前说过,OGNL上下文包含ActionContext和ValueStack。我们先来说说ActionContext,它是一个大Map,里面装有4个小Map,分别是application、session、request和attr。我们做个测试时往里面存数据然后再取数据。

——我们部署好struts2之后,在index.jsp中使用struts标签库。在正文中写
<s:debug></s:debug>
,它相当于一个a标签,点击前往可以查看整个OGNL上下文里面的数据。

——我们在动作类的方法中存入数据,存数据有多重方法,总之是获得对应的Map,然后往里面存数据。

public String data(){
//往大Map里面,也就是ActionContext里存数据
ActionContext context=ActionContext.getContext();
context.put("actionContextKey", "actionContextValue");

//第1种方式:往小Map里面存数据,比如ActionContext里的小Mapsession
Map<String,Object> s1=context.getSession();
s1.put("sessionKey1", "sessionValue1");
//第2种方式:往小Map里面存数据,比如ActionContext里的小Mapsession
HttpSession s2=ServletActionContext.getRequest().getSession();
s2.setAttribute("sessionKey2", "sessionValue2");

//第1种方式,往application里存
Map<String,Object> a1=context.getApplication();
a1.put("applicationKey1", "applicationValue1");
//第2种方式,往application里存
ServletContext a2=ServletActionContext.getServletContext();
a2.setAttribute("applicationKey2", "applicationValue2");

return SUCCESS;
}


——然后在index.jsp页面里面获取数据:

<!-- 取ActionContext大Map里面的值,用#+key -->
<s:property value="#actionContextKey"/><br>
<!-- 取小Map里的值,需要先取到对应的session和application等。而这些都是大Map的key,所以操作如下 -->
<s:property value="#session.sessionKey1"/><br>
<s:property value="#session.sessionKey2"/><br>
<s:property value="#application.applicationKey1"/><br>
<s:property value="#application.applicationKey2"/><br>


源代码:JavaEE ActionContext存取数据示例

2、ValueStack存取数据。

——和上述案例类似,我们在动作类的方法中写如下代码,用于获取ValueStack的引用,有3种方式,很明显用第三种ActionContext直接获取更方便。而且我们打印出他们3个引用的hashcode发现是同一个,这就是线程安全的体现。

public String data(){
//第1种方式:获取ValueStack的引用,通过request间接获取
HttpServletRequest request1=ServletActionContext.getRequest();
ValueStack vs1=(ValueStack)request1.getAttribute("struts.valueStack");
System.out.println(vs1.hashCode());

//第2种方式:获取ValueStack的引用,通过request间接获取
ActionContext context=ActionContext.getContext();
Map<String,Object> request2=(Map<String,Object>)context.get("request");
ValueStack vs2=(ValueStack)request2.get("struts.valueStack");
System.out.println(vs2.hashCode());

//第3种方式:获取ValueStack的引用,通过actionContext直接获取
ValueStack vs3=context.getValueStack();
System.out.println(vs3.hashCode());

return SUCCESS;
}


——获取ValueStack的引用后,我们采用压栈操作存数据。

//压栈操作
vs3.push(new User("Andy",22));
vs3.push(new User("eric",23));


——我们可以看到后push的在栈顶。



——我们在jsp中运用的时候,操作如下,默认取第一个,当然可以指定索引。

<!-- 取ValueStack值,不需要#,它从最上面栈顶依次找,找到就行 -->
<s:property value="username"/><br> //结果是eric
// 这里有一个索引是[1],其实意思是把第一个栈顶的元素pop出去,如果是[2]那么就把最上面2个元素pop出去再取值,下方我们重新做试验
<s:property value="[1].username" /> //结果是Andy


——采用setValue存数据,第一个参数是表达式,加#表示存在ActionContext里,不加#表示存在ValueStack里面,存在ValueStack里面其实是给里面对应的变量赋值,也就是说如下面代码所示,如果ValueStack里面没有username变量,那么就会报错。紧接着上面的代码,我们发现我们存在ValueStack里面的test2其实是覆盖了原先有的eric(因为它是从栈顶开始的第一个username属性对应的值,被setValue覆盖了)。

vs3.setValue("#username", "test1");
vs3.setValue("username", "test2");


上面第一句代码产生的效果就是存在了ActionContext里面:



——采用set方法设置数据。

//set方法,如果栈顶是Map元素,直接key和value赋值过去,如果栈顶不是,那么就创建一个放在栈顶
vs3.set("s1", new User("tom",30));


然后在jsp中取值的时候,操作:

<!-- 取栈顶Map里面的数据 -->
<s:property value="s1.username" />


注意:如果
s:property
不指定的话默认取栈顶元素(或元素对应的地址)。

——我们来验证ValueStack取值时候的索引是把元素pop出去,而不是取第几个值的意思。

//压栈操作
vs3.push(new User("Andy",22));
vs3.push(new User("eric",23));

//setValue
vs3.setValue("#username", "test1"); vs3.setValue("username", "test2");

//set方法,如果栈顶是Map元素,直接key和value赋值过去,如果栈顶不是,那么就创建一个放在栈顶 vs3.set("s1", new User("tom",30));


——经过上面的代码操作,我们的ValueStack的从栈顶往下应该是Map、User(test2)、User(Andy)。如下图。



——接着,我们在jsp中取值,按照道理好像是我们先从栈顶向下取了test2,然后下标为1也就是第二个是Andy,然后下标为2也就是第三个是
空的没有值


<s:property value="username"/><br>
<s:property value="[1].username" /><br>
<s:property value="[2].username" /><br>


但是,事实上我们得到的值是:

test2
test2
Andy


——原理就在于,我们第一句话取值,表示从栈顶向下取第一个遇到的值,就是test2。第二句话取值,有个下标1,表示把栈顶第1个元素pop掉,然后生成一个新的ValueStack的List,从上向下取值,发现还是test2。第三句话取值,有个下标2,表示把栈顶最上面两个元素pop掉,也就是一个HashMap和User都pop掉了,然后取值,发现取到的是Andy。这里面起作用的函数就是cutStack,它根据下标的数字cut掉栈顶几个元素然后生成一个新的栈。

——另外,我们上面使用的struts标签
s:property
,其实本质上都是调用了findValue方法,这个寻找顺序和以前讲过的类似,就是page、request、session、application。

3、Struts2对EL表达式查找顺序的改变。核心意思就是:一般情况下,我们写
${name}
这样一个EL表达式,程序会先去pageScope中找,然后依次是requestScope、sessionScope和applicationScope。但是在Struts2种,查找顺序就不是这样了,而是pageScope、requestScope、ValueStack、ContextMap(ActionContext)、sessionScope和applicationScope。也就是说,我们在动作类中如果不把数据放入4个域中,一样可以通过EL表达式获取到数据,因为它会被放到ValueStack里。

——我们在动作类中写:

public class MyAction extends ActionSupport {
private String name="动作类中的name";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
……
}


——然后,我们刷新页面发现会自动添加到ValueStack里面了。



——我们在jsp页面使用EL表达式
${name }
获取数据,就能获取到
动作类中的name
。核心是因为Struts2对原来的request进行了包装。

4、iterator操作符。

——现在动作类里面整一个List。

public class MyAction extends ActionSupport {
private List<User> users;

public List<User> getUsers() {
return users;
}

public void setUsers(List<User> users) {
this.users = users;
}

public String data(){
users=new ArrayList<User>();
users.add(new User("Eric",20));
users.add(new User("Andy",21));
users.add(new User("Tom",22));

return SUCCESS;
}
}


——然后,在jsp中直接使用,我们说这个List没有放在4个域中,但是被放到了ValueStack里,所有在jsp中使用是可以取到值的。下面有两种方法,第一种是没有var属性的,那么每次遍历的元素都会被压到ValueStack里面,所以我们就直接用属性名username等取值。如果有var属性,那么会将它们放到ActionContext中,其中var的值作为Key,元素内容作为Value,所以我们取值需要用
#
,而且是
#key+属性
的形式取值。

<table>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
</tr>
<s:iterator value="users" status="stat">
<tr>
<td><s:property value="#stat.count" /></td>
<td><s:property value="username" /></td>
<td><s:property value="age" /></td>
</tr>
</s:iterator>
</table>

<table>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
</tr>
<s:iterator value="users" var="u" status="stat">
<tr>
<td>${stat.count }</td>
<td><s:property value="#u.username" /></td>
<td><s:property value="#u.age" /></td>
</tr>
</s:iterator>
</table>


5、OGNL的投影:添加过滤条件。很少用到,做个了解即可。

——比如上面迭代的例子,我们做如下修改,也就是获取所有age>21的元素。这里面有3种,
?#
表示全部,
^#
表示匹配第一个,
$#
表示匹配最后一个。

<s:iterator value="users.{?#this.age>21}" status="stat">


——下面表示只提取username属性的值。

<s:iterator value="users.{username}" status="stat">


6、struts2的其他标签。

——set标签。作用是var当做key,value里面数据当做值存到ActionContext里去。但是写成如下的样子,虽然在ActionContext里能找到,但是值是null。因为value里面是一个OGNL表达式,需要转成字符串。

<s:set var="name1" value="test1"></s:set>


正确写法:

<s:set var="name1" value="'test1'"></s:set>


可以看到ActionContext李:



——action标签。指定动作类名称,并不会执行,取值默认是false。

<s:action name="action1" executeResult="true"></s:action>


——if/else标签。

<s:set var="score" value="'C'"></s:set>
<s:if test="#score=='A'">优秀</s:if>
<s:elseif test="#score=='B'">良好</s:elseif>
<s:else>不妙</s:else>


——url标签。

// 页面上直接输出i-am-value
<s:url value="i-am-value"></s:url>
//把action全路径输出来,显示的是/Day01_ValueStack/data.action,不存储
<s:url action="data"></s:url>
//不显示,只存储,存在ActionContext里,key就是customUrl
<s:url action="data" var="customUrl"></s:url>


有的时候我们存储了action的全路径后,就使用
<a href='<s:property value="#customUrl" />'>点我到action去</a>
’。而不直接使用硬编码的
<a href='${pageContext.request.contextPath}/data.action'>点我到action去</a>
。因为我们时候要做伪静态页面,也就是把网址最后修改成html结尾。那么这个时候我们使用
#customUrl
就自动转换了,而采用硬编码的方式就会出错。

伪静态的设置在struts.xml里:

<constant name="struts.action.extensions" value="html" ></constant>


——我们还可以添加get参数:

<s:url action="actionData" var="customUrl">
<s:param name="username" value="'andy'"></s:param>
</s:url>


然后我们在使用这个
#customUrl
链接的地方就会发现,这个链接后面自动添加了一个参数。



7、几种符号的使用。

——#。去contextMap里面的值用。还有在OGNL创建Map对象时用,如
<s:radio list="#{'0':'男','1':'女'}" />


——$。jsp的EL表达式用。还有在struts.xml配置文件中也用,比如配置下载文件的那地方
${@java.net.URLEncoder.encode(filename)}
。这里的filename是我们在动作类里面定义的,我们知道动作类是默认在ValueStack的栈顶的,所以里面的变量也在ValueStack里面,所以我们才能取到值。

——%。用的地方是把字符串强制当做OGNL表达式。我们知道反过来把OGNL表达式转成字符串就是加一对单引号,反过来操作的话就是用这里的%{}。比如
<s:textfield value="%{username}" />


8、结合ModelDriver、struts表单的源代码。

——在struts2-core-2.3.15.3.jar核心包里面有一个xhtml的包,里面就是UI主题,还有一个simple的主题。

——我们可以针对某一个表单或者全部表单进行设置。但是需要注意的是如果设置成simple主题的话,前面label名称没有了需要自己写,而且每个表单都不换行了需要自己换行。我们在struts.xml文件中设置常量效果是一样的,只不过设置常量就意味着整个项目的表单都会生效:
<constant name="struts.ui.theme" value="simple"></constant>


<s:form action="registerAction" theme="simple">
……
</s:form>


源代码:JavaEE struts表单和ModelDriver以及主题

9、防止表单重复提交。我们防止重复提交的话,一般是通过验证码,第一次生成的时候,放一份验证码在Session里面,然后提交后比较了就删除session里面的验证码,这样再提交就没有比较就判断出错了,防止重复提交。

——在struts2里面,我们可以用tokenSession来防止重复提交,它的原理就是只处理第一次的提交,后面的直接忽略掉。我们先在表单中增加一个token的标签表单,是隐藏的。

<s:form action="loginAction">
<s:token></s:token>
<s:textfield name="username" label="用户名"></s:textfield>
<s:submit value="提交"></s:submit>
</s:form>


然后,在struts.xml对应的action里面配置这个拦截器。

<package name="p_name_1" extends="struts-default">
<action name="loginAction" class="com.hello.web.action.MyAction" method="login">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="tokenSession"></interceptor-ref>
<result>/success.jsp</result>
</action>
</package>


我们在动作类的方法中写的一个输出语句,每次提交都会经过动作类打印这条语句,但是配置了以上代码之后,我们提交成功后,后退浏览器再提交的时候,语句就不打印了,也就是说只打印一次,动作类的方法只执行1次,这就是防止重复提交的内容。

源代码:JavaEE Struts2利用tokenSession防止重复提交
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  struts