值棧詳解(ValueStack)
什麼是值棧
之前web階段,在servlet裡面進行操作,把資料放到域物件裡面,在頁面中使用el表示式獲取到。域物件在一定範圍內,存值和取值。
在struts2裡面提供了本身的一種儲存機制,類似於域物件,是值棧,可以存值和取值。
- 在action裡面把資料放到值棧裡面,在頁面中獲取到值棧的資料。
注意:對於存值,你可以使用servlet中的三種域物件,使用這三種域物件都可以在jsp頁面中獲取到。同時你使用struts2裡面的儲存機制同樣能夠實現上面的效果,但是並不是一定要你使用struts2裡面的儲存方法。目的要讓你知道struts2也能夠辦到同樣存值取值的操作。只是你說會struts2 但是你不會值棧,這是說不過去的!!!
servlet和action的區別:
Servlet:
預設在第一次訪問的時候建立,只建立一次,是一個單例物件!!
Action:
一樣是訪問的時候建立物件,每次訪問action的時候都會建立新的action物件,建立多次,是一個多例項物件!!
值棧的儲存位置:
- 每次訪問action的時候都會建立action物件。
- 在每個action物件裡面都會有一個值棧物件。(注意:每個action物件只有一個)
獲取值棧
獲取值棧物件有多種方式!
- 常用的方式:使用ActionContext物件裡面的方法(getValueStack())獲取值棧物件。
目的驗證在同一個action值棧只有一個:
public class pr_action{
public String execute(){
ActionContext context=ActionContext.getContext();
ValueStack vs1=context.getValueStack();
ValueStack vs2=context.getValueStack();
System.out.println(vs1==vs2);
}
}
輸出結果:
true
值棧內部結構
棧:先進後出!
最上面是棧頂的元素,向棧裡面放資料的操作叫做壓棧。
所以,你沒有猜錯,值棧也是用的這種資料結構!但是值棧分為兩個部分,root和context,root就是棧的資料結構,同時context也是
root專業叫做ObjectStack(物件棧)
context專業叫做ContextMap(Map棧),Map型別的棧。(在我們訪問裡面的物件的時候,會通過出棧的方式取東西,效率比較低,一般我們不會用)
值棧分為兩個部分:
- 第一個部分 root ,平常我們用的值棧就是操作root裡面的內容,很少去操作context。結構是List集合。
可以通過ctrl+shift+t搜尋類,來檢視它的父類。
很驚奇你會發現root物件型別繼承了ArrayList這個類
- 第二部分 context ,結構是Map集合
下面來講講context這個物件:
context裡面儲存的都是些固定的值,有以下幾個:
key————value
request—>最底層是request(HttpServletRequest),但是在這裡的request是RequestMap型別的。如果你在HttpServletRequest型別中賦了值,那麼在RequestMap中照樣能夠讀取到。
session—>HttpSession物件引用
application—>ServletContext物件引用
parameters—>傳遞相關的引數
attr—>使用attr操作,能夠獲取域物件裡面的值,獲取域範圍最小裡面的值。
要想檢視到值棧的結構可以用除錯(debug)的方法!
通過struts2的標籤<s:debug></s:debug>
,我們可以很清楚的看到值棧確實分為兩個部分,一個root,一個context,上面我們已經講解了context的儲存內容
,下面我們就來講講root的儲存內容:
我們已經瞭解到root是一個棧的儲存結構。下面是root預設儲存的內容:
action物件引用
DefaultTextProvider
也就是說在root的棧頂還儲存著action的引用,為什麼會這個儲存呢??
其實它只是為了能夠在值棧裡面取出action,能夠在action裡面取出值棧,僅此而已!
向值棧裡面放資料
向值棧裡面放資料時,其實儲存的位置是在root域裡面
向值棧放資料有多種方式,往往我們只用其中一種
- 第一種 獲取值棧物件,呼叫值棧物件裡面的 set 方法
ValueStack stack=ActionContext.getContext().getValueStack();
stack.set("username","FireLang");
在用set方法新增值棧資料之後,會在root棧頂多一個HashMap物件
- 第二種 獲取值棧物件,呼叫值棧物件裡面的 push 方法
呼叫push之後,就會在root棧頂放入一個String型別的物件!
- 第三種 在action定義變數,生成變數的get方法(主要)
public class pr_action{
private String name;
public String getName(){
return name;
}
public String execute(){
name="FireLang";
return "success";
}
}
在用第三種方法之後,struts2並不會在值棧root的棧頂放入新的物件,它的儲存路徑還是儲存在action裡面,所以這就起到了資源的合理應用,當想要獲取name屬性的時候,就會在值棧裡面呼叫action的getName方法。這也就是為什麼會在值棧裡面儲存action的原因了。
向值棧中放物件
實現步驟:
第一步:定義物件變數
第二步:生成變數的get方法
第三步:在執行的方法裡面向物件中設定值
向值棧中放List物件
第一步:定義List集合變數
第二步:生成變數的get方法
第三步:在執行的方法裡面向List集合設定值
action的程式碼:
public class Pr_fangList {
private List<User> lu;
public String execute(){
lu=new ArrayList<User>();
User u1=new User();
u1.setName("胡藝寶");
u1.setPassword("123");
lu.add(u1);
User u2=new User();
u2.setName("胡家源");
u2.setPassword("456");
lu.add(u2);
System.out.println(lu);
return "success";
}
public List<User> getLu() {
return lu;
}
}
jsp中程式碼:
<!-- 文章截止到目前還沒有說到el表示式為什麼能夠取到值棧裡面的資料,在文章後續會解釋的 -->
${lu[0].name }
${lu[0].password }<br>
<hr><br>
${lu[1].name }
${lu[1].password }
<s:debug></s:debug>
從值棧的root裡面取資料
使用struts2的標籤+ognl表示式獲取值棧資料
<s:property value="ognl表示式"/>
獲取字串
步驟:
服務端程式碼:
//服務端程式碼是為了讓欄位被存入ValueStack中
//欄位的前提條件是必須設定get方法
public class Pr_getString {
private String name;
public String getName() {
return name;
}
public String execute(){
name="胡藝寶";
return "success";
}
}
客戶端jsp程式碼:
<s:property value="name"/><!-- 這裡的name是ognl表示式。表示獲取action中的name欄位值,必須要寫get方法,因為欄位讀或者寫的功能按照規定,都必須通過讀或者寫方法來給變數賦值 -->
獲取物件
aciton中的程式碼:
//再次強調必須要get方法。
public class Pr_getUserObj {
private User us;
public User getUs() {
return us;
}
public String execute(){
us=new User();
us.setName("胡藝寶");
us.setPassword("FireLang");
return "success";
}
}
jsp中的程式碼:
<s:property value="us.name" /><br><!-- value中的值是ognl表示式 -->
<s:property value="us.password" /><!-- 獲取到us物件後,再獲取us中的name屬性和password屬性,再次強調獲取欄位基本上都是按照規定通過get和set方法進行操作! -->
獲取List集合
通過ognl+struts標籤獲取List集合共有三種方式
服務端程式碼:
public class Pr_getList {
private List<User> usl=null;
public List<User> getUsl() {
return usl;
}
public String execute(){
usl=new ArrayList<User>();
User tempUser=new User("胡藝寶", "123465");
usl.add(tempUser);
return "success";
}
}
第一種:
客戶端程式碼:
//這種程式碼非常不好,在很多時候你永遠不可能知道服務端傳來的List裡面到底有多少引數。
<s:property value="usl[0].name"/>
<s:property value="usl[0].password"/>
第二種方式:
類似jstl中的foreach標籤
<s:iterator value="usl">
<s:property value="name"/>
<s:property value="password"/>
<br><hr><br>
</s:iterator>
第三種方式:
/*第三種方式較第二種方式多加了一個var,根本區別就是iterator把遍歷出來的值放進了值棧的第二部分空間context,contex因為是Map結構的所以要加上一個鍵作為取值方式,也就是var的值作為context的鍵,其實這種方式算是一種優化,不用在root中去拿值了。而第二種方式還會到root裡面去拿值。速度沒有在context中的快*/
<s:iterator value="usl" var="singleus">
<s:property value="#singleus.name"/>
<s:property value="#singleus.password"/>
</s:iterator>
set方法和push方法的取值(會用)
set方法的取值
//服務端程式碼:
public class Pr_getUserSet {
private User us;
public String execute(){
us=new User("胡藝寶","789");
ActionContext.getContext().getValueStack().set("us", us);
ActionContext.getContext().getValueStack().set("lang", "FireLang");;
return "success";
}
}
<!-- 客戶端程式碼: -->
<!-- 直接從root域裡面取值 -->
<s:property value="us"/><br><br>
<s:property value="lang"/>
//執行結果:
User [name=胡藝寶, password=789]
FireLang
push方法取值
//伺服器端程式碼:
//這裡要注意的是push方法是直接把資料存放在root中的。不像set一樣可以通過key來取值。
//push的取值方法有點特殊,是通過直接把棧頂元素取出來的。
public class Pr_getUserSet {
private User us;
public String execute(){
us=new User("胡藝寶","789");
ActionContext.getContext().getValueStack().push(us);
ActionContext.getContext().getValueStack().push("FireLang");
return "success";
}
}
<!-- 客戶端jsp程式碼 -->
<s:property value="[0].top"/>//取第一個
<s:property value="[1].top"/>//取第二個,這裡的top是root的域實體物件名稱,也就是List物件的名稱
執行結果:
FireLang User [name=胡藝寶, password=789]
增強一個類常用的方式:裝飾者模式,動態代理,繼承
用EL表示式取值:
el取值能夠獲取到action裡面的值,具體原理就是,它重寫的request,進行了request域的增強,裡面進行了以下操作:
el取值時,如果request域裡面能夠找到目標值,那麼就把值返回到頁面。如果在request域裡面不能夠取到目標值,那麼就通過值棧獲取。
ActionContext.getContext().getValueStack().findValue(“key”);如果查詢到值就返回資料。這裡的request是通過HttpServletRequestWrapper重寫過。
所以在el表示式獲取Action裡面存取的值的時候效率沒有通過Struts標籤來的快。推薦用struts標籤和ongl來獲取Action裡面的資料。
el在獲取Action裡面的值時,action裡面的欄位也必須提供get方法。否則無法獲取到值。
//服務端程式碼:
public class Pr_getList {
private List<User> usl=null;
public List<User> getUsl() {
return usl;
}
public String execute(){
usl=new ArrayList<User>();
User tempUser=new User("胡藝寶", "123465");
usl.add(tempUser);
return "success";
}
}
<!-- 客戶端jsp程式碼: -->
<!-- 在使用jstl標籤以前要先匯入jstl標籤庫,這裡使用的jstl標籤庫是1.2版本 -->
<c:forEach items="${usl }" var="temp">
${temp.name }<br>
${temp.password }
</c:forEach>
//這個是增強request的原始碼:
ActionContext ctx = ActionContext.getContext();
Object attribute = super.getAttribute(key);
if (ctx != null && attribute == null) {
boolean alreadyIn = isTrue((Boolean) ctx.get(REQUEST_WRAPPER_GET_ATTRIBUTE));
// note: we don't let # come through or else a request for
// #attr.foo or #request.foo could cause an endless loop
if (!alreadyIn && !key.contains("#")) {
try {
// If not found, then try the ValueStack
ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.TRUE);
ValueStack stack = ctx.getValueStack();
if (stack != null) {
attribute = stack.findValue(key);//這就是el能夠獲取到值棧裡面的關鍵
}
} finally {
ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE);
}
}
}
return attribute;
ognl兩個符號的使用#號和%的使用
使用#來獲取context中的資料
//服務端的程式碼:
public class Pr_getRequestByContext {
private User us;
public User getUs() {
return us;
}
public String execute(){
us=new User("胡藝寶", "123456789");
((RequestMap)ActionContext.getContext().getValueStack().getContext().get("request")).put("user", us);//把資料儲存到context的request中
//其中你也可以通過ActionContext.getContext().get("request")獲取到的效果和上面的一樣。
System.out.println(ServletActionContext.getRequest().getAttribute("user"));//驗證是否能夠在HttpServletRequest中獲取到user,事實證明能夠獲取到。猜測RequestMap的最底層依賴了HttpServletRequest
return "success";
}
}
<!-- 這個是jsp客戶端的程式碼 -->
<s:property value="#request.user"/>
執行後能夠成功獲取user資料
%號的使用
場景:在struts2表單標籤裡面使用ognl表示式,如果直接在struts2表單標籤裡面使用ognl表示式會不識別,只有使用%號之後才會識別。
//服務端程式碼就用上面那個
<!-- 客戶端jsp程式碼,成功執行輸出後的程式碼: -->
<s:textfield name="username" value="%{#request.user.name}"></s:textfield>
OK!!!完成!!
相關推薦
值棧詳解(ValueStack)
什麼是值棧 之前web階段,在servlet裡面進行操作,把資料放到域物件裡面,在頁面中使用el表示式獲取到。域物件在一定範圍內,存值和取值。 在struts2裡面提供了本身的一種儲存機制,類似於域物件,是值棧,可以存值和取值。 在action裡面把資料
10.5-全棧Java筆記:常見流詳解(三)
java上節我們講到「Java中常用流:緩沖流」,本節我們學習數據流和對象流~ 數據流數據流將“基本數據類型變量”作為數據源,從而允許程序以與機器無關方式從底層輸入輸出流中操作java基本數據類型。 DataInputStream和DataOutputStream提供了可以存取與機器無關的所有Java基礎類
【linux】Valgrind工具集詳解(十):SGCheck(檢查棧和全域性陣列溢位)
一、概述 SGCheck是一種用於檢查棧中和全域性陣列溢位的工具。它的工作原理是使用一種啟發式方法,該方法源於對可能的堆疊形式和全域性陣列訪問的觀察。 棧中的資料:例如函式內宣告陣列int a[10],而不是malloc分配的,malloc分配的記憶體是在堆中。 SGCheck和Me
“全棧2019”Java第三十章:數組詳解(下篇)
允許 頭條 維數 數組 學習小組 更多 同步 技術 intel 難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文鏈接 “全棧2019”Java第三十章:數組詳解(下篇)
“全棧2019”Java第三十章:陣列詳解(下篇)
難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第三十章:陣列詳解(下篇) 下一章 “全棧
“全棧2019”Java第二十九章:陣列詳解(中篇)
難度 初級 學習時間 10分鐘 適合人群 零基礎 開發語言 Java 開發環境 JDK v11 IntelliJ IDEA v2018.3 文章原文連結 “全棧2019”Java第二十九章:陣列詳解(中篇) 下一章 “全
Android動畫詳解(二)插值器
在上一篇Android動畫詳解(一)補間動畫中我們提到過一個叫插值器的東西,看名字一頭霧水完全不知道是什麼神奇玩意。其實用人話翻譯過來就是速度模型或者速度曲線的意思。為動畫設定插值器就是設定動畫的速度模型,就是設定它是怎麼動的,先加速再加速呀、一直減速呀、勻速的
【模型詳解】AutoEncoder詳解(七)——棧式自編碼:Stacked AutoEncoder
更新時間:2018-12-05 前言 之前介紹了AutoEncoder及其幾種拓展結構,如DAE,CAE等,本篇部落格介紹棧式自編碼器。 模型介紹 普通的AE模型通過多層編碼解碼過程,得到輸出,最小化輸入輸出的差異從而使模型學到有用的特徵。但是這種AE結構又
linux system函式返回值詳解(1)
曾經的曾經,被system()函式折磨過,之所以這樣,是因為對system()函數了解不夠深入。只是簡單的知道用這個函式執行一個系統命令,這遠遠不夠,它的返回值、它所執行命令的返回值以及命令執行失敗原因如何定位,這才是重點。當初因為這個函式風險較多,故拋棄不用,改用其他的方法。這裡先不說我用了什麼方法,這裡
java.util包詳解(二)——Connection接口
操作 相同 元素 叠代 cat roo soft true nbsp Connection接口介紹 Connection接口是java集合的root接口,沒有實現類,只有子接口和實現子接口的各種容器。主要用來表示java集合這一大的抽象概念。 Connection接
WindowManager.LayoutParams 詳解(轉載)
鼠標 chang 結束 name ble edi status 條件 backup WindowManager.LayoutParams 是 WindowManager 接口的嵌套類;繼承於 ViewGroup.LayoutParams 。它的內容十分豐富。其實Window
JQuery中$.ajax()方法參數詳解(轉載)
瀏覽器 object 服務器 字符串 false type: 要求為String類型的參數,請求方式(post或get)默認為get。註意其他http請求方法,例如put和 delete也可以使用,但僅部分瀏覽器支持。timeout: 要求為Number類型的參數,設置請求超時時
Maven詳解(轉載)
odi 解決辦法 ctrl 世界 maven倉庫 避免 -- epo 時間 Maven詳解 一.前言 以前做過的項目中,沒有真正的使用過Maven,只知道其名聲很大,其作用是用來管理jar 包的。最近一段時間在項目過程中使用Maven,用Maven構建的web項目,
Java 多線程詳解(三)------線程的同步
alt 來看 監聽 介紹 創建進程 java 多線程 system ima 關鍵字 Java 多線程詳解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多線程詳解(二)------如何創建進
實現高性能糾刪碼引擎 | 糾刪碼技術詳解(下)
糾刪碼引擎 基礎知識 深入優化 技術 工程師 作者介紹: 徐祥曦,七牛雲工程師,獨立開發了多套高性能糾刪碼/再生碼編碼引擎。柳青,華中科技大學博士,研究方向為基於糾刪碼的分布式存儲系統。前言:在上篇《如何選擇糾刪碼編碼引擎》中,我們簡單了解了 Reed-Solomon Codes(RS 碼
MongoDB執行計劃分析詳解(1)
mongo smu pre als comm 計劃 -- {} direct 正文 queryPlanner queryPlanner是現版本explain的默認模式,queryPlanner模式下並不會去真正進行query語句查詢,而是針對query語句進行執行計劃分析並
elastic-job詳解(一):數據分片
count 任務 不同的 應該 center shc 偶數 int ext 數據分片的目的在於把一個任務分散到不同的機器上運行,既可以解決單機計算能力上限的問題,也能降低部分任務失敗對整體系統的影響。elastic-job並不直接提供數據處理的功能,框架只會將分片項分配至各
Linux的SOCKET編程詳解(轉)
readv lose 服務 網絡字節序 返回值 quest avi 取數 key Linux的SOCKET編程詳解 1. 網絡中進程之間如何通信 進 程通信的概念最初來源於單機系統。由於每個進程都在自己的地址範圍內運行,為保證兩個相互通信的進 程之間既互不幹擾又
elastic-job詳解(三):Job的手動觸發功能
方法 idt image blog per tle cnblogs ack display elastic-job的任務都是使用quartz來觸發的,quartz表達式一般都是定期執行。但有時候一些周期較長的任務,比如一天一次,幾小時一次的任務,我們需要等待很久才能觸發一次
綜合運用: C++11 多線程下生產者消費者模型詳解(轉)
並發 rep 生產 我會 交流 模型 操作 const ref 生產者消費者問題是多線程並發中一個非常經典的問題,相信學過操作系統課程的同學都清楚這個問題的根源。本文將就四種情況分析並介紹生產者和消費者問題,它們分別是:單生產者-單消費者模型,單生產者-多消費者模型,多生產