Struts2_4_ActionMap与ValueStack详解_Struct2的EL及常用标签_防止表单数据重复提交

本文详细讲解了Struts2中的ActionMap和ValueStack数据存储机制,包括如何存储和获取数据,以及Struts2中的EL表达式。此外,还介绍了Struts2常用标签的使用,并探讨了如何防止表单数据重复提交,提供了Servlet和Struts2两种解决方案。
摘要由CSDN通过智能技术生成

接着Struts2_3_day的讲

注:使用Struts2的< s:debug>< /s:debug>就可获取数据储存的分布图

StrutsPrepareAndExecuteFilter都会创建一个ActionContext和ValueStack对象, 所以Struts2的数据存储分为两类: ActionMap(contextMap)以及ValueStack; ActionMap中都是以map的形式存取数据, 而ValueStack是以Stack的方式存取数据(底层就是List)

ActionMap数据操作

  1. 利用ActionMap存储数据
    ActionMap是一个大的map, 而这个大map中又存有application, session, request, parameters, attr的小map, 所以在ActionMap中存放数据的时候既能存入大map中, 也能存入小map中, 在小map中还能存入map, 形成多层的存储, 如下代码操作:
1.直接在ActionMap中存入数据, 使用ActionMap的静态方法getContext获取ActionMap对象, 然后直接put
	ActionMap context = ActionMap.getContext();
	context.put("contextMap", "contextMap");
	
2.往ActionMap中的session对象中存入数据
	使用ActionMap中的session, session也是一个map
		Map<String, Object> sessionAttribute = context.getSession();
		sessionAttribute.put("sessionMap1", "sessionMap1");
	使用原始HttpSession对象存储数据
		HttpSession session = ServletActionContext.getRequest().getSession();
		session.setAttribute("sessionMap2", "sessionMap2");
		
3.往ActionMap的application中存入数据
	使用使用ActionMap中的ServletContext域
		Map<String, Object> applicationAttribute = context.getApplication();
		application.put("applicationMap1", "applicationMap1");
	使用原始ServletContext对象
		ServletContext application = ServletActionContext.getServletContext();
		application.setAttribute("applicationMap2", "applicationMap2");

注: 无论使用ServletActionContext中的存储方式, 还是使用ActionMap.getContext().getXXX()的方式, 他们所指向的存储区域都是一样的, 例如上面的applicationMap1和applicationMap2, 他们二者的数据都是存放在同一片储存区域的, 同理sessionMap1与sessionMap2也是一样的

  1. 获取ActionMap中数据
    可以直接使用Map的get, 或者使用域对象的getAttribute方法来获取ActionMap中数据, 但是大多数使用OGNL表达式在jsp中获取ActionMap数据, 例如:
// 使用OGNL表达式获取域对象数据, OGNL表达式必须写在Struts2的标签中
<%@ taglib uri="/struts-tags" prefix="s" %>
<s:property value="#session.sessionMap1">
//在使用OGNL表达式取用ActionMap的数据的时候, 使用"#" 

//借用上面代码的存储方式, 因为sessionMap1,以及sessionMap2是存入session中, 所以直接使用#session.sessionMap1
//数据的查找顺序是: 先去ActionMap中查找session区域, 找到之后在session区域中查找是否具有key为sessionMap1的数据

//同理, 当查找applicationMap1也是直接使用:<s:property value="#application.applicationMap1">

ValueStack数据操作

  1. 利用ValueStack存储数据
    正如前面所说的, ValueStack就是一个Stack结构, 当我们向ValueStack中存入数据的时候就是进行入栈, 出栈的操作

下面先介绍获取ValueStack对象引用的3种常见方式:

1.使用<s:debug></s:debug>之后发现, 在request中存有ValueStack的引用, 所以可以使用get的方式获取:
	ActionContext context = ActionContext.getContext();
	Map<String, Object> request = (Map<String, Object>) context.get("request");
	ValueStack vs = (ValueStack)request.get("struts.valueStack");
	
2.使用ServletActionContext获取request对象, 然后再通过getAttribute获取:
	ServletRequest request = ServletActionContext.getRequest();
	ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack");
	
3.直接使用ActionContext对象的getValueStack方法获取:
	ValueStack vs = ActionContext.getContext().getValueStack();
	
注: 通过上面的3种方式获取的vs都是同一个对象

当我们获取到ValueStack对象之后就可以对ValueStack对象做push, peek/pop操作以此达到存储数据的目的, 例如:

ValueStack vs = ActionContext.getContext().getValueStack();
vs.push(new Student("A", 18)); //对一个Student对象做入栈操作

注: 默认动作类对象会自动存入ValueStack中, 因此可以通过OGNL表达式获取默认动作对象的属性方法(对该属性一定要写set/get方法)

  1. 获取ValueStack中数据
    当使用动作类进行ValueStack存储数据的时候, 一定要记住: 如果直接想通过OGNL表达式获取, 此时数据只具有request的作用域范围, 当使用页面重定向后, 就不能访问到刚才动作方法中存入ValueStack中的数据
	1.可以使用pop/peek直接获取ValueStack数据或者jsp中使用标签的方式获取ValueStack数据; 
	2.jsp中操作: 使用< s:property value="name" />这里的name是一个OGNL表达式, 用于获取Student中的name属性;
	3.与获取ActionContext数据不同的是, value后面的参数少了一个"#", **"#"是作为获取ValueStack与ActionMap中数据的一种区别**;
	4.在ValueStack中, 查找方式是: 在Stack中依次遍历每个对象, 查看当前对象的属性是否是value后面的参数值,
		如果是就代表找到, 就不再往后查找. 例如: 当动作类中也具有一个name属性, 但是现在栈顶是一个Student对象, 当找到Student对象中的name属性的时候就不再往后找, 即使动作类中也具有name;
	5.在OGNL表达式中可以多级访问: 比如ValueStack中存入的数据是一个Map对象map, Map对象中存有一个Student对象s1, 当我们想要获取此时Student对象的name属性的时候, 就可以写:< s:property value="s1.name" />
  1. ValueStack的set与setValue方法的区别
set方法:
ValueStack vs = ActionContext.getContext().getValueStack();
vs.push(new Student("A", 19));
vs.set("s1", new Student("B", 20));

如上述代码, 当vs使用set操作的时候, 操作的元素是ValueStack中的元素.
如果栈顶是一个Map, 就直接将一个key为s1, value为Student对象存入Map中
如果栈顶不是一个Map, 则ValueStack新建Map<String, Object>对象, 然后将key为s1, value为Student对象存入Map中

当连续多次vs.set()的时候, 所有的set的对象都是存入一个Map对象中, 例如:
	vs.set("s1", new Student("A", 20));
	vs.set("s2", new Student("B", 10));
	vs.set("s3", new Student("C", 24));
	
当使用OGNL表达式访问栈中map中的元素, 采用: <s:property value="s1.name"/>, 而不是<s:property value="#s1.name">, 这里是在ValueStack中取元素, 而不是ActionMap
在获取元素的时候, 元素对象一定要写set/get方法
这里别理解为连续set是建立多个map, 只有在下面这种情况下, 才是建立多个Map
	vs.set("s1", new Student("A", 20)); //新建一个map
	vs.push(new Student("C", 24));
	vs.set("s2", new Student("B", 10)); //又新建一个map

setValue方法:
setValue对ValueStack中存储元素值的修改操作:
	vs.setValue("name", "X");
	将栈中第一个name元素修改为X, 借用上面代码, 此处的修改是将"C"变为"X"
	setValue的第一个参数是一个OGNL表达式
	
setValue对ActionMap中存储元素值的修改操作:
	vs.setValue("#name", "X");
	这里修改的name属性, 是对ActionMap中key为name的value进行修改
  1. ValueStack的findValue
    在JSP上使用OGNL表达式获取ActionMap或者ValueStack中的内容的时候, 都是执行如下操作
vs.findValue("name"); //在ValueStack中查找名为name属性的值
vs.findValue("#name"); //在ActionMap中查找key为name的value值

Struts中的EL

JSP中的EL表达式获取的是请求域中对象(page -> request -> session -> application)
Struts2的StrutsRequestWrapper对request重新做了封装, 对getAttribute进行改写, 当获取完request域中对象后, 会先去ValueStack, 然后是ActionMap中寻找需要的对象

Struts2对EL表达式进行了改写使得获取对象变为: page -> request -> ValueStack -> ActionMap -> session -> application

注: 当使用OGNL表达式查找属性值的时候, 如果写做< s:property value=“name” />, 它会先去ValueStack中查找, 当没找到的时候再去ActionMap中, name作为key找一遍.

Struts2中#, $, %的使用

  • #
OGNL表达中
	获取ActionMap中的Key为key的value值:
		<s:property value="#name"> //获取Key为name的value值
	创建Map对象的时候, 主要用于CheckBox, radio的多条件选择:
		<s:radio list="#{ 'male':'男', 'female':'女' }">
  • $
JSP中使用EL表达式:
	${name}
在xml中配置文件名, 或者使用静态函数(避免在JSP中写<%%>代码)
	${@java.lang.Math.random()}
  • %
强制将一个字符串看做是OGNL:
	<s:property value="%{name}">

Struts2中常用标签(使用OGNL配合使用)

  • iterator标签
    主要用于迭代遍历元素, 类似于JSP中的foreach标签, 下面举例说明:
Action类:
//使用list模拟从数据库提取Student数据, 显示在结果视图中
	public class DemoAction extends ActionSupport{
		private List<Student> students;
		public String findAll(){ //action方法
			students = new ArrayList<Student>(3);
			students.add(new Student("A", 20));
			students.add(new Student("B", 30));
			students.add(new Student("C", 10));
			return SUCCESS;
		}
		public List<Student> getStudents(){
			return students;	
		}
		public void setStudents(List<Student> students){
			this.students = students;
		}
	}
//JSP中iterator操作
	<s:iterator value="students" var="s" status="vs">
		<tr>
			<td><s:property value="#vs.index"/><br/></td>
			<td><s:property value="#s.name"/><br/></td>
			<td><s:property value="#s.age"/><br/></td>	
		</tr>
	</s:iterator>

注:

1.在遍历的过程中具有两种操作用于暂时存储需要遍历的变量(方便获取该变量的内部属性):
	a.使用ActionMap: 当指定在iterator中指定var属性的时候,Struts2会将var作为key, 当前变量作为value存入ActionMap中, 便于后面通过OGNL表达式获取该变量的内部属性, 因为key是固定的, 所以下一个元素存入ActionMap中会将上一个元素的value覆盖, 这样做也是为了减少空间占用,遍历结束后会将最后存在ActionMap中的变量remove
	b.使用ValueStack: 如果没有指定var, 那么Struts2会直接将该变量压入栈中, 当使用完该变量进行pop操作, 原因同上
	
2.status主要是用来记录遍历的数据属性(以status作为key, 数据属性作为value存入ActionMap中):
	status具有index, count, odd, even, first, last属性, 表示下标, 遍历的数据和, 奇数位, 事件, 第一个值, 最后一个值, 具体用法自行百度
	
3.在上面例子中, 可以使用value="name"的OGNL表达式来输出变量属性, 也可以使用EL表达式${name}来输出变量属性
  • set标签
    与JSP中的set标签一样, 就是声明一个变量, 然后对该变量赋值, 设置访问范围
<s:set value="a" var="1111" scope="session"/>
<s:property value="#1111"/>
声明一个变量"1111", 赋值为"a"的字符串, 设定访问范围为session

set设置的变量是存入Map中的, value代表属性值, var代表Key值, scope取值范围application, session, request, page, action, 当scope不写的时候默认是action, ActionMap与request中各存一份
  • action标签
    在JSP中调用action方法: < s:action name=“action1” executeResult=“true”/> ,executeResult表示是否显示执行动作结果, 默认值false

  • url和a标签
    url标签主要用于获取url地址
    a标签用于向一个url地址进行跳转, 就< a href="…"/ >

例:
	<s:url action="action1" value="action1" var="url">
		<s:param name="username" value=" 'test' "></s:param>
	</s:url>
	
	action: 获取值为action1的动作请求地址, 类似于EL表达式: ${pageContext.request.contextPath}/action1.action
	value: url标签中的value用于输出value的值, 此时的value后面的参数不是OGNL表达式, 只是一个普通字符串; 而param中的value代表get方式添加参数, 会自动编码 如:username?test 
	var: 将值为url作为key存入ActionMap中
	
	使用上面的url
	<a href="<s:property value='#url'>">点击</a>
	
	当使用<s:a>标签的时候就类似于使用<s:url>标签与<a>标签的结合品
	<s:a action="action1">
		<s:param name="username" value=" 'test' ">
	</s:a>
  • if/else标签:
    if/else标签的用法与JSP中标签的用法一致
<s:if test=" name=='A' ">A</s:if>
<s:elseif test=" name=='B' ">B</s:if>
<s:else>C

防止表单数据重复提交

Servlet中的解决方式:

  • 用户第一次提交表单数据之前先将数据存入Session域中, 当用户数据提交之后就删除Session数据, 当用户重复提交的时候会发现表单中数据与Session中数据无法做比较, 最后判定数据重复提交, 对后面提交的数据不作处理

Struts2中的解决方式

  • 使用token/tokenSession拦截器(Struts2提供的一个拦截器)
使用token/tokenSession拦截器的时候, 会产生一个令牌隐藏在Session域中, 当用户提交数据后令牌消失, 下一次提交找不到对应的令牌, 所以最后判定为用户数据重复提交
如上面看到的, Struts2中防止重复提交与Servlet中防止重复提交的思想是一致的

此处说明一个问题就是:
	token/tokenSession不在Struts2的defaultStack中, 在使用的时候需要配置defaultStack拦截器
	token与tokenSession的区别:
		token做拦截的时候, 当用户提交后有一个页面跳转, 有一个错误反馈, 告诉用户数据重复提交
		tokenSession做拦截的时候, 不去做页面跳转和错误反馈, 无论用户提交多少次表单数据, tokenSession只取第一次提交的数据

下面使用token举例说明(使用tokenSession的时候, 只需将token改为tokenSession):

index.jsp:
	<s:form action="save">
		<s:token></s:token>	<%此标签用于生成隐藏令牌%>
		<s:textfield name="name" label="用户名"></s:textfield>
		<s:submit value="保存"></s:submit>
	</s:form>
	
Struts.xml:
	<struts>
		<constant name="struts.devMode" value="true"></constant>
		<package name="p1" extends="struts-default">
			<action name="save" class="com.action.DemoAction">
				<interceptor-ref name="defaultStack"></interceptor-ref>
				<!--配置token拦截器-->
				<interceptor-ref name="token"></interceptor-ref>
				<result type="redirect">/success.jsp</result>
				<!--使用token的时候必须做invalid.token跳转, 跳转到error.jsp中-->
				<!--在使用tokenSession的时候不需要做invalid处理-->
				<result name="invalid.token">/error.jsp</result>
			</action>
		</package>
	</struts>

到此Struts2的学习就结束了, 在后面的学习中, 我会将一个没有讲到的内容以及一些容易犯错的地方补充上.

上面有错, 还请指出, 如果认为我写的还不错, 还请点个赞, 多多支持一下, O(∩_∩)O~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值