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