【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标签库。在正文中写
——我们在动作类的方法中存入数据,存数据有多重方法,总之是获得对应的Map,然后往里面存数据。
——然后在index.jsp页面里面获取数据:
源代码:JavaEE ActionContext存取数据示例
2、ValueStack存取数据。
——和上述案例类似,我们在动作类的方法中写如下代码,用于获取ValueStack的引用,有3种方式,很明显用第三种ActionContext直接获取更方便。而且我们打印出他们3个引用的hashcode发现是同一个,这就是线程安全的体现。
——获取ValueStack的引用后,我们采用压栈操作存数据。
——我们可以看到后push的在栈顶。
——我们在jsp中运用的时候,操作如下,默认取第一个,当然可以指定索引。
——采用setValue存数据,第一个参数是表达式,加#表示存在ActionContext里,不加#表示存在ValueStack里面,存在ValueStack里面其实是给里面对应的变量赋值,也就是说如下面代码所示,如果ValueStack里面没有username变量,那么就会报错。紧接着上面的代码,我们发现我们存在ValueStack里面的test2其实是覆盖了原先有的eric(因为它是从栈顶开始的第一个username属性对应的值,被setValue覆盖了)。
上面第一句代码产生的效果就是存在了ActionContext里面:
——采用set方法设置数据。
然后在jsp中取值的时候,操作:
注意:如果
——我们来验证ValueStack取值时候的索引是把元素pop出去,而不是取第几个值的意思。
——经过上面的代码操作,我们的ValueStack的从栈顶往下应该是Map、User(test2)、User(Andy)。如下图。
——接着,我们在jsp中取值,按照道理好像是我们先从栈顶向下取了test2,然后下标为1也就是第二个是Andy,然后下标为2也就是第三个是
但是,事实上我们得到的值是:
——原理就在于,我们第一句话取值,表示从栈顶向下取第一个遇到的值,就是test2。第二句话取值,有个下标1,表示把栈顶第1个元素pop掉,然后生成一个新的ValueStack的List,从上向下取值,发现还是test2。第三句话取值,有个下标2,表示把栈顶最上面两个元素pop掉,也就是一个HashMap和User都pop掉了,然后取值,发现取到的是Andy。这里面起作用的函数就是cutStack,它根据下标的数字cut掉栈顶几个元素然后生成一个新的栈。
——另外,我们上面使用的struts标签
3、Struts2对EL表达式查找顺序的改变。核心意思就是:一般情况下,我们写
——我们在动作类中写:
——然后,我们刷新页面发现会自动添加到ValueStack里面了。
——我们在jsp页面使用EL表达式
4、iterator操作符。
——现在动作类里面整一个List。
——然后,在jsp中直接使用,我们说这个List没有放在4个域中,但是被放到了ValueStack里,所有在jsp中使用是可以取到值的。下面有两种方法,第一种是没有var属性的,那么每次遍历的元素都会被压到ValueStack里面,所以我们就直接用属性名username等取值。如果有var属性,那么会将它们放到ActionContext中,其中var的值作为Key,元素内容作为Value,所以我们取值需要用
5、OGNL的投影:添加过滤条件。很少用到,做个了解即可。
——比如上面迭代的例子,我们做如下修改,也就是获取所有age>21的元素。这里面有3种,
——下面表示只提取username属性的值。
6、struts2的其他标签。
——set标签。作用是var当做key,value里面数据当做值存到ActionContext里去。但是写成如下的样子,虽然在ActionContext里能找到,但是值是null。因为value里面是一个OGNL表达式,需要转成字符串。
正确写法:
可以看到ActionContext李:
——action标签。指定动作类名称,并不会执行,取值默认是false。
——if/else标签。
——url标签。
有的时候我们存储了action的全路径后,就使用
伪静态的设置在struts.xml里:
——我们还可以添加get参数:
然后我们在使用这个
7、几种符号的使用。
——#。去contextMap里面的值用。还有在OGNL创建Map对象时用,如
——$。jsp的EL表达式用。还有在struts.xml配置文件中也用,比如配置下载文件的那地方
——%。用的地方是把字符串强制当做OGNL表达式。我们知道反过来把OGNL表达式转成字符串就是加一对单引号,反过来操作的话就是用这里的%{}。比如
8、结合ModelDriver、struts表单的源代码。
——在struts2-core-2.3.15.3.jar核心包里面有一个xhtml的包,里面就是UI主题,还有一个simple的主题。
——我们可以针对某一个表单或者全部表单进行设置。但是需要注意的是如果设置成simple主题的话,前面label名称没有了需要自己写,而且每个表单都不换行了需要自己换行。我们在struts.xml文件中设置常量效果是一样的,只不过设置常量就意味着整个项目的表单都会生效:
源代码:JavaEE struts表单和ModelDriver以及主题
9、防止表单重复提交。我们防止重复提交的话,一般是通过验证码,第一次生成的时候,放一份验证码在Session里面,然后提交后比较了就删除session里面的验证码,这样再提交就没有比较就判断出错了,防止重复提交。
——在struts2里面,我们可以用tokenSession来防止重复提交,它的原理就是只处理第一次的提交,后面的直接忽略掉。我们先在表单中增加一个token的标签表单,是隐藏的。
然后,在struts.xml对应的action里面配置这个拦截器。
我们在动作类的方法中写的一个输出语句,每次提交都会经过动作类打印这条语句,但是配置了以上代码之后,我们提交成功后,后退浏览器再提交的时候,语句就不打印了,也就是说只打印一次,动作类的方法只执行1次,这就是防止重复提交的内容。
源代码:JavaEE Struts2利用tokenSession防止重复提交
——我们部署好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防止重复提交
相关文章推荐
- JAVAWEB开发之Struts2详解(四)——ognl与valueStack(重点)、Struts常用标签、防止表单重复提交、Struts2中内置json插件
- Struts标签-OGNL-EL-ValueStack-ActionContext之间的区别
- JavaWeb -- Struts1 使用示例: 表单校验 防表单重复提交 表单数据封装到实体
- JavaWeb开发知识总结(表单重复提交,数据分页)
- JavaWeb -- Struts1 使用示例: 表单校验 防表单重复提交 表单数据封装到实体
- 传智播客Java web之 JSP基础之EL表达式注释以及错误查找
- javaweb多行数据提交问题
- Struts2 ValueStack & ActionContext & OGNL 关系小结
- java web从零单排第十七期《struts2》数据标签库(1)
- ActionContext、ValueStack、OGNL
- java数组-顺序插入数据及二分法查找数据
- Java-web防止重复提交、动态验证码
- 严重: action: null java.lang.ClassNotFoundException: org.springframework.web.struts.ContextLoaderPlugIn
- struts2.x的ActionContext,ValueStack和常用标签介绍
- Java-web放重复提交、动态验证码
- 那些年java web开发中遇到的问题(4)---如何实现表单提交(插入数据到mysql)
- struts2手动防止数据重复提交,使用struts标签取session中的值
- java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/action-servlet.xml]
- 从调试角度理解ActionContext、OgnlContext、OgnlValueStack的关系
- java web 提交数据乱码