1. 程式人生 > >值棧詳解(ValueStack)

值棧詳解(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 生產者消費者問題是多線程並發中一個非常經典的問題,相信學過操作系統課程的同學都清楚這個問題的根源。本文將就四種情況分析並介紹生產者和消費者問題,它們分別是:單生產者-單消費者模型,單生產者-多消費者模型,多生產