Struts2框架之值栈

 什么是值栈 ValueStack

   回顾web阶段数据交互:
        客户端提交数据到服务器端:request接受数据,BeanUtils实体封装。
        服务器端数据在页面上显示:在服务器端将数据存储到request域中,页面中通过el+jstl完成数据展示。
        
    struts2阶段数据交互:
        客户端提交数据到服务器端:属性驱动和模型驱动。
        服务器端数据 在页面上显示:在服务器端将数据存储到值栈中,在页面中通过ognl+struts2标签数据展示。

   由此可见:
        request域==值栈
        el表达式==ognl表达式
        jstl标签==struts2标签
    
    
    值栈是什么?
        值栈是服务器与客户端进行数据交互的数据中心,用于存储数据。
        在服务器将数据存储到值栈中 在页面中从值栈取出数据进行展示。


        值栈ValueStack是Struts2提供接口规范,于此同时也提供了该接口的实现OgnlValueStack。
        
    值栈的生命周期?

     
            
        
值栈的内部结构

    获得值栈对象:

        ActionContext context = ActionContext.getContext();
        VAlueStack valueStack = context.getValueStack();

    值栈的内部结构:
        Root栈:ArrayList
        context栈:map

public class ValueStackAction extends ActionSupport implements ModelDriven<User>{
	//获得值栈对象 查看其内部结构
	public String show(){
		//获得Action的上下文对象
		ActionContext context = ActionContext.getContext();
		//获得值栈对象
		ValueStack valueStack = context.getValueStack();
		System.out.println(valueStack);
		return NONE;
	}
}

值栈对象何时创建?ValueStack 和 ActionContext是什么关系(了解)


    源码分析:
        --->StrutsPrepareAndExecuteFilter
        --->public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        --->prepare.createActionContext(request, response);
        --->ValueStack stack = createValueStack();
        
    ActionContext内部维护值栈的引用

如何获得值栈对象ValueStack

第一种方式:(掌握)

        ActionContext context = ActionContext.getContext();
        VAlueStack valueStack = context.getValueStack();

第二种方式:(了解)
        通过request域对象 request.getAttribute("struts.valueStack");
        
        源码剖析:
            --->StrutsPrepareAndExecuteFilter
            --->public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            --->execute.executeAction(request, response, mapping);
            --->dispatcher.serviceAction(request, response, mapping);
            --->request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
                public static final String STRUTS_VALUESTACK_KEY = "struts.valueStack"

public class ValueStackAction extends ActionSupport implements ModelDriven<User>{
	//测试值栈的获取方式
	public String show(){
		//获得Action的上下文对象
		ActionContext context = ActionContext.getContext();
		//获得值栈对象
		ValueStack valueStack1 = context.getValueStack();
		//通过request获取
		ValueStack valueStack2 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");

		System.out.println(valueStack1);
		System.out.println(valueStack2);

		return NONE;
	}
}

 

如何手动向值栈保存数据 (了解)

 1、向Root栈存储数据

public class ValueStackAction extends ActionSupport implements ModelDriven<User>{

	//测试向Root栈存储数据(了解)
	public String show(){
		//获得值栈对象
		ValueStack valueStack = ActionContext.getContext().getValueStack();
		//获得内部的Root栈
		CompoundRoot root = valueStack.getRoot();
		//存数据
		root.add("itcast");		//存储到尾部
		root.add(0, "itheima"); //存储到头部(栈顶)
		//CompoundRoot的存取数据的API
		root.push("xxx"); //压栈 add(0, o);
		root.peek(); //获得栈顶的值 return get(0);
		root.pop(); //弹栈 return remove(0);

		return NONE;
	}
}

 

2、向context栈存储数据

public class ValueStackAction extends ActionSupport implements ModelDriven<User>{
	
	//测试向context栈存储数据(了解)
	public String show(){
		//获得值栈对象
		ValueStack valueStack = ActionContext.getContext().getValueStack();
		//获得context栈
		Map<String, Object> map = valueStack.getContext();
		map.put("xxx", "XXX");
		map.get("xxx");

		return NONE;
	}
}

3、valueStack对象维护着向值栈存取数据的API

public class ValueStackAction extends ActionSupport implements ModelDriven<User>{
	//测试valueStack对象向值栈存储值的APi
	public String show(){
		//获得值栈对象
		ValueStack valueStack = ActionContext.getContext().getValueStack();
		//压栈
		valueStack.push("xxx"); //root.push(o);
		//取值
		valueStack.peek();//return root.peek();
		valueStack.pop();//return root.pop()

		//向root栈设置  将key-value键值对封装到一个Map中 在将Map压栈
		/*
		 * 源码分析:
		 * 	public void set(String key, Object o) {
		        Map setMap = retrieveSetMap();	该map是哪个??????
		        setMap.put(key, o);		将数据存储到map中
		    }

		    private Map retrieveSetMap() {
		        Map setMap;		定义一个map
		        Object topObj = peek();	取出栈顶的值
		        if (shouldUseOldMap(topObj)) {	
		            setMap = (Map) topObj;
		        } else {
		            setMap = new HashMap();		创建一个Map
		            setMap.put(MAP_IDENTIFIER_KEY, "");		向Map中存储一个值
		            push(setMap);	将Map压到栈顶
		        }
		        return setMap;	返回Map
		    }

		    private boolean shouldUseOldMap(Object topObj) {
		    	//topObj instanceof Map : 判断栈顶的元素对象是否是一个Map集合 
		    	//((Map) topObj).get(MAP_IDENTIFIER_KEY) :判断该Map是否存在标志
		        return topObj instanceof Map && ((Map) topObj).get(MAP_IDENTIFIER_KEY) != null;
		    }
		 * 	
		 * 
		 * 
		 */

		valueStack.set("aaa", "AAA");
		valueStack.push("xxxx");
		valueStack.set("bbb", "BBB");

		//通过valueStack的findValue(key/propertyName)方法取数据

		return NONE;
	}
}

 

struts2框架会将哪些数据自动保存到值栈中(重点)    

1、引入struts2的标签<s:debug/>   该标签可以在页面中直观看到值栈的内部数据

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入Struts2标签--%>
<%@taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
    <title>Title</title>
</head>
    <body>
        <%--引入struts2的标签<s:debug/>--%>
        <s:debug/>
    </body>
</html>

2、struts2框架会自动将哪些数据存储到Root栈(重点)
        将当前访问的Action对象自动压栈
            作用:Action的get方法的属性值 可以通过ognl表达式方便获取
        将当前模型对象自动压到栈顶
            作用:模型对象压到栈顶 通过ognl表达式方便取出模型对象的属性值

3、struts2框架会自动将哪些数据存储到context栈(了解)
        request对象
        response对象
        各种域数据
        请求参数
        ... ...
        维护本次请求的所有相关数据

public class ValueStackAction extends ActionSupport implements ModelDriven<User>{
	
	private String username = "张三";
	public String getUsername() {
		return username;
	}
	
	private User user;
	public User getUser() {
		return user;
	}
	
	private List<User> userList;
	public List<User> getUserList() {
		return userList;
	}
	
	private int num = 3;
	public int getNum() {
		return num;
	}

	//测试自动保存数据到值栈
	public String show(){
		user = new User("tom", 18);

		model.setName("传智播客");
		model.setAge(15);
		
		ActionContext.getContext().getValueStack().getContext().put("company", "itcast");
		
		userList = new ArrayList<>();
		User u1 = new User("小红", 18);
		User u2 = new User("小兵", 38);
		userList.add(u1);
		userList.add(u2);
		
		return SUCCESS;
	}
}

如何取值栈的数据 (Ognl+struts2标签)(重点)  

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<s:debug/>
	
	<h1>取出普通数据name</h1>
	<s:property value="name"/>
	<h1>取出user中的name</h1>
	<s:property value="user.name"/>
	
	<h1>取出context中的company数据</h1>
	<s:property value="#company"/>
	
	<h1>取出userList中的小兵</h1>
	<s:property value="userList[1].name"/>
	
	<h1>遍历userList</h1>
	<!-- 
		会将集合中的每一个元素对象 依次临时存储到栈顶
	 -->
	<%-- <s:iterator value="userList">
		<s:property value="name"/>==<s:property value="age"/><br/>
	</s:iterator> --%>
	
	<!-- 
		var: 变量名称  临时使用var指定的字符串作为key在context栈存储一份
	 -->
	<s:iterator value="userList" var="user">
		<s:property value="#user.name"/>==<s:property value="#user.age"/><br/>
	</s:iterator>
	
	<!-- 
		s:iterator可以模拟普通for循环
		var:将循环数字 赋值给变量i
		step:步长
		status:封装是循环的状态数据  将状态对象以指定的名称存储到context栈
	 -->
	<s:iterator begin="1" end="10" var="i" step="2" status="status">
		<%-- <s:property value="#i"/><br/> --%>
		<s:property value="#status.last"/>
	</s:iterator>
	
	<h1>struts2的if标签</h1>
	<s:if test="num>10">
		大于10
	</s:if>
	<s:elseif test="num==10">
		等于10
	</s:elseif>
	<s:else>
		小于10
	</s:else>
	
	<h1>el从值栈取值</h1>
	${name }<br/>
	${user.name }
	
</body>
</html>

注意:什么符号都不加 代表从Root栈取值,#代表直接从context栈取值。

 

为什么 EL也能访问值栈中的数据 (重点)(面试)

原因:StrutsRequestWrapper
    ${} 底层调用getAttribute方法
    源码:
        request = prepare.wrapRequest(request);
        request = dispatcher.wrapRequest(request);


        //BufferedReader reader = new BufferedReader(new FileReader());
        StrutsRequestWrapper request = new StrutsRequestWrapper(request);
        
        增强的是getAttribute方法:
        public Object getAttribute(String key) {
            //调用原来的request的方法
            Object attribute = super.getAttribute(key); //从request域中获得值
            if (attribute == null) {
                attribute = stack.findValue(key);
            }
            return attribute;
        }

猜你喜欢

转载自blog.csdn.net/aiyowei1106/article/details/81543864