1. 程式人生 > >Struts2框架之值棧

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;
        }