1. 程式人生 > >Struts2的ValueStack(值棧)的原理

Struts2的ValueStack(值棧)的原理

1.     資料傳輸背後機制:ValueStack(值棧)
 

在這一切的背後,是因為有了ValueStack(值棧)!

ValueStack基礎:OGNL
要了解ValueStack,必須先理解OGNL(Object Graphic Navigatino Language)!

OGNL是Struts2中使用的一種表示式語言,它可以用於JSP的標籤庫中,以便能夠方便的訪問各種物件的屬性;它用於介面將引數傳遞到Action(並進行型別轉換)中;它還可以用於struts2的配置檔案中!所以,非常有必要理解OGNL的基本機制。

Root物件
OGNL稱為物件圖導航語言。所謂物件圖,即以任意一個物件為根,通過OGNL可以訪問與這個物件關聯的其它物件。如:

package cn.com.leadfar.struts2.actions;

public class User {

    private String username;

    private Group group;

    public String getUsername() {

       return username;

    }

    public void setUsername(String username) {

       this.username = username;

    }

    public Group getGroup() {

       return group;

    }

    public void setGroup(Group group) {

       this.group = group;

    }

}

 
 
package cn.com.leadfar.struts2.actions;

public class Group {

    private String name;

    private Organization org;

    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

    public Organization getOrg() {

       return org;

    }

    public void setOrg(Organization org) {

       this.org = org;

    }

}

 
 
package cn.com.leadfar.struts2.actions;

public class Organization {

    private String orgId;

    public String getOrgId() {

       return orgId;

    }

    public void setOrgId(String orgId) {

       this.orgId = orgId;

    }

}

上面三個類,描述了通過一個User物件,可以導航到Group物件,進而導航到Organization物件,以User物件為根,一個物件圖如下所示:

User(root)

   -- username

   -- group

      -- name

      -- org

         -- orgId

在真實的環境下,這個物件圖可能會極其複雜,但是通過基本的getters方法,都應該能夠訪問到某個物件的其它關聯物件。【物件圖的導航,必須通過getters方法進行導航】

下述程式碼將建立一個User物件,及其相關的一系列物件:

       User user = new User();

       Group g = new Group();

       Organization o = new Organization();

        o.setOrgId("ORGID");

       g.setOrg(o);

       user.setGroup(g);
 

如果通過JAVA程式碼來進行導航(依賴於getters方法),導航到Organization的orgId屬性,如下所示:

//用JAVA來導航訪問

user.getGroup().getOrg().getOrgId();

【注意:導航的目的,是為了獲取某個物件的值或設定某個物件的值或呼叫某個物件的方法!】

【注意:OGNL表示式語言的真正目的,是為了在那些不能寫JAVA程式碼的地方執行JAVA程式碼,或者是為了更方便地執行JAVA程式碼】

利用OGNL進行導航的程式碼如下:

       //利用OGNL表示式訪問

       String value = (String)Ognl.getValue("group.org.orgId", user);

Ognl.getValue()方法的第一個引數,就是一條OGNL表示式,第二個引數是指定在表示式中需要用到的root物件!

完整程式碼如下:

    public void testOgnl01() throws Exception{

       User user = new User();

       user.setUsername("張三");

       //利用OGNL表示式訪問user物件的username屬性

       String value = (String)Ognl.getValue("username", user);

       log(value);

    }

    public void testOgnl02() throws Exception{

       User user = new User();

       Group g = new Group();

       Organization o = new Organization();

       o.setOrgId("ORGID");

       g.setOrg(o);

       user.setGroup(g);

       //用JAVA來導航訪問

       log(user.getGroup().getOrg().getOrgId());

       //利用OGNL表示式訪問

       String value = (String)Ognl.getValue("group.org.orgId", user);

       log(value);

    }

    public void testOgnl03() throws Exception{

       User user = new User();

       Group g = new Group();

       Organization o = new Organization();

       o.setOrgId("ORGID");

       g.setOrg(o);

       user.setGroup(g);

       //用JAVA來導航訪問

       log(user.getGroup().getOrg().getOrgId());

       //也可以在表示式中使用#root來代表root物件

       String value = (String)Ognl.getValue("#root.group.org.orgId", user);

       log(value);

    }

    private void log(Object o){

       System.out.println(o);

    }
 

Context物件
在OGNL的表示式中,有可能需要訪問到多個毫不相干的物件,這時候,我們需要給OGNL傳遞一個Map型別的物件,把表示式中需要用到的物件放到Map中即可!這個Map物件,稱為context。

要在表示式中訪問到context中的物件,需要使用“#物件名稱”的語法規則。

如:

    public void testOgnl04() throws Exception{

       User user = new User();

       user.setUsername("張三");

       Group g = new Group();

       Organization o = new Organization();

       o.setOrgId("ORGID");

       g.setOrg(o);

       user.setGroup(g);

       User user2 = new User();

       user2.setUsername("李四");

       /**

        * 所謂context其實就是一個Map型別的物件。主要是因為在OGNL中,不支援多個root物件,那麼

        * 如果需要在表示式中訪問更多毫不相干的物件時,只能通過一個Map來把這些物件統一傳遞給OGNL。

        */

       Map context = new HashMap();

       context.put("u1", user);

       context.put("u2", user2);

       //在表示式中需通過“#+物件的名稱”來訪問context中的物件

       //如果表示式中沒有用到root物件,那麼可以用任意一個物件代表root物件!

       String value = (String)Ognl.getValue("#u1.username + ',' + #u2.username", context,new Object());

       log(value);

    }

    public void testOgnl05() throws Exception{

       User user = new User();

       user.setUsername("張三");

       Group g = new Group();

       Organization o = new Organization();

       o.setOrgId("ORGID");

       g.setOrg(o);

       user.setGroup(g);

       User user2 = new User();

       user2.setUsername("李四");

       User user3 = new User();

       user3.setUsername("王五");

       Map context = new HashMap();

       context.put("u1", user);

       context.put("u2", user2);

       //給OGNL傳遞root物件及context物件,以便解釋對應的表示式

       String value = (String)Ognl.getValue("#u1.username + ',' + #u2.username + ',' + username", context,user3);

       log(value);

    }
 

利用OGNL表示式進行賦值
 

OGNL表示式也可以用於賦值操作。

    public void testOgnl06() throws Exception{

       User user = new User();

       //呼叫setValue()方法來進行賦值

       //第一個引數:OGNL表示式

       //第二個引數:root物件

       //第三個引數:要賦的值     

       Ognl.setValue("username", user, "張三");

       log(user.getUsername());

    }

    public void testOgnl07() throws Exception{

       User user = new User();

       Map context = new HashMap();

       context.put("u", user);

       //呼叫setValue()方法來進行賦值

       //第一個引數:OGNL表示式

       //第二個引數:context物件

        //第三個引數:root物件

       //第四個引數:要賦的值

       Ognl.setValue("#u.username", context, new Object(), "張三");

       log(user.getUsername());

    }

    public void testOgnl08() throws Exception{

       User user = new User();

       Map context = new HashMap();

       context.put("u", user);

       //利用賦值符號"="來進行賦值

       Ognl.getValue("#u.username = '李四'", context, new Object());

       log(user.getUsername());

    }

    public void testOgnl09() throws Exception{

       User user1 = new User();

       User user2 = new User();

       Map context = new HashMap();

       context.put("u1", user1);

       context.put("u2", user2);

       //在一個表示式中可以用逗號分隔,同時執行多個表示式

       Ognl.getValue("#u1.username = '李四',#u2.username='王五'", context, new Object());

       log(user1.getUsername());

       log(user2.getUsername());

    }
 

利用OGNL呼叫物件的方法
 

    //************************* OGNL呼叫物件的方法 *****************************//

    public void testOgnl10() throws Exception{

       User user = new User();

       //如果是呼叫root物件的方法,可以直接使用方法的名稱來呼叫方法

       Integer value = (Integer)Ognl.getValue("addSomething(1,1)", user);

       log(value);

    }

    public void testOgnl11() throws Exception{

       User user = new User();

       user.setUsername("李四");

       //如果是呼叫root物件的方法,可以直接使用方法的名稱來呼叫方法

       String value = (String)Ognl.getValue("getUsername()", user);

       log(value);

    }

    public void testOgnl12() throws Exception{

       User user = new User();

       Ognl.getValue("setUsername('王五')", user);

       String value = (String)Ognl.getValue("getUsername()", user);

       log(value);

    }

    //************************* OGNL呼叫靜態方法和變數 *********************//

    public void testOgnl13() throws Exception{

       User user = new User();

       user.setUsername("王五");

       //呼叫靜態變數

       //注意:out是System中的靜態變數,out是PrintStream型別的一個物件

       //而println()則是out這個物件中的例項方法(不是靜態方法)

       //呼叫靜態方法,需要在類名和變數名前面加上@來呼叫,對於例項方法,用"."來呼叫

       Ognl.getValue("@[email protected](username)", user);

    }

    public void testOgnl14() throws Exception{

       User user = new User();

       user.setUsername("wangwu");

       //呼叫靜態方法,注意使用全路徑類名

        Ognl.getValue("@[email protected](@[email protected](username))", user);

    }
 

利用OGNL訪問陣列、集合物件
 

    public void testOgnl15() throws Exception{

       Object root = new Object();

       Map context = new HashMap();

       //利用OGNL建立java.util.List物件

       List list = (List)Ognl.getValue("{123,'xxx','kdjfk'}", context, root);

       context.put("list", list);

       //利用OGNL建立陣列

       int[] intarray = (int[])Ognl.getValue("new int[]{23,45,67}", context, root);

       context.put("intarray", intarray);

       //利用OGNL表示式建立java.util.Map物件

       Map mapvalue = (Map)Ognl.getValue("#{'listvalue':#list,'intvalue':#intarray}", context, root);

       context.put("mapvalue", mapvalue);

       //利用OGNL表示式訪問這些陣列和集合物件

       Ognl.getValue("@[email protected](#list[1])", context,root);

       Ognl.getValue("@[email protected](#intarray[2])", context,root);

       Ognl.getValue("@[email protected](#mapvalue.listvalue[0])", context,root);

       Ognl.getValue("@[email protected](#mapvalue['intvalue'][0])", context,root);

    }

    public void testOgnl16() throws Exception{

       List root = new ArrayList();

       User user1 = new User();

       user1.setUsername("張三");

       User user2 = new User();

       user2.setUsername("李四");

       root.add(user1);

       root.add(user2);

       //如果root物件是List型別

       log(Ognl.getValue("#root[0].username", root));

       log(Ognl.getValue("#root[1].username", root));

    }
 

更多的特性,請參考官方的文件
 

OGNL官方文件地址:http://www.opensymphony.com/ognl/html/LanguageGuide/index.html

應用:ValueStack
 

理解ValueStack的基本機制!對各種現象作出解釋。

ValueStack實際上就是對OGNL的封裝,OGNL主要的功能就是賦值與取值,Struts2正是通過ValueStack來進行賦值與取值的!


ValueStack是一個介面,而OgnlValueStack是strtus2中的預設實現。ValueStack中的資料,分兩個部分存放:root和context(這與OGNL中的概念一致),同時ValueStack暴露相關的介面:

void setValue(String expr, Object value);

Object findValue(String expr);

用來通過OGNL表示式對ValueStack中的資料進行操作!

ValueStack中的root物件是CompoundRoot,CompoundRoot繼承了ArraryList,提供了額外的方法:push()和pop()方法,用來對root物件中所包含的資料進行存取!

public class CompoundRoot extends ArrayList {

    public CompoundRoot() {

    }

    public CompoundRoot(List list) {

        super(list);

    }

    public CompoundRoot cutStack(int index) {

        return new CompoundRoot(subList(index, size()));

    }

    public Object peek() {

        return get(0);

    }

    public Object pop() {

        return remove(0);

    }

    public void push(Object o) {

        add(0, o);

    }

}
 

正是通過這兩個方法,CompoundRoot變成了一個棧結構!壓棧操作,將導致物件被放到CompoundRoot的第0個元素上(第0個元素是棧頂),其它物件被依次往後移動;出棧操作,將導致CompoundRoot的第0個元素被移除(即棧頂元素被彈出),其它物件被依次往前移動!

OGNL不支援多個root物件,而struts2能夠支援多個root物件,它對OGNL做了擴充套件。

如果某個OGNL表示式被傳遞給ValueStack(即呼叫ValueStack的setValue或findValue方法),而表示式中包含有對root物件的訪問操作,ValueStack將依次從棧頂往棧底搜尋CompoundRoot物件中所包含的物件,看哪個物件具有相應的屬性,找到之後,立刻返回。

在Struts2中,一個請求在最終到達Action的方法之前,Action物件本身會被壓入ValueStack(實際上就是放到ValueStack的CompoundRoot中),所以Action物件是CompoundRoot中的一個元素。看下面的程式碼:

public class UserAction {

    private String username;

    private Integer age;

    private boolean valid;

    //檢視使用者的詳細資訊

    public String detail(){

       username = "張三";

       age = 18;

       valid = true;

       return "detail";

    }
 

在Action中,給Action的username/age/valid賦值。Detail頁面如下:

username:<s:property value="username"/> <br/>

valid:<s:property value="valid"/> <br/>

age:<s:property value="age"/> <br/>
 

上述JSP頁面將能正確將它們的值取出。<s:property value=”ognl表示式”/>。在s:property標籤中的OGNL表示式,最終會交給ValueStack來解釋。username就是一個OGNL表示式,意思是呼叫root物件的getUsername()方法。Struts2將自動搜尋CompoundRoot中有哪些元素(從第0個元素開始搜尋),檢測這些元素是否有getUsername()方法,如果第0個元素沒有getUsername()方法,將繼續搜尋第1、2、3……個元素是否有getUsername()方法。

在上面的例子中,CompoundRoot中只有一個物件,就是userAction物件,而這個物件中正好有getUsername()方法,所以,上述JSP程式碼將能夠將值正確取出。

再看下面的例子:

public class UserAction {

    private String username;

    private String name;

    //檢視使用者的詳細資訊

    public String detail(){

       username = "張三";

       name = "王五";

       User u = new User();

       u.setUsername("趙毅");

       ActionContext.getContext().getValueStack().push(u);

       return "detail";

    }
 

在上面這個UserAction的程式碼中,我們直接呼叫ActionContext.getContext().getValueStack().push()方法,把一個User物件(這個物件擁有getUsername()和setUsername()方法)直接壓入到ValueStack中,這時候,在ValueStack的CompoundRoot中將有兩個元素:第0個元素是剛剛壓入的user物件[趙毅],而第1個元素是userAction物件[張三],如果在JSP中使用下面的表示式來取值:

<s:property value=”username”/> ,那麼輸出的值將是“趙毅”!道理上面已經講過了,struts2將會從第0個元素開始搜尋CompoundRoot中的物件,第0個元素正是剛剛壓入的那個user物件!

如果在JSP中使用<s:property value=”name”/>來取值,將取出“王五”,因為第0個元素user物件沒有name屬性,所以,會繼續搜尋第1個元素userAction物件,在這個物件中就有name屬性了!

再看下面的程式碼:

public class UserAction {

    private String username;

    //檢視使用者的詳細資訊

    public String detail(){

       username = "張三";

       List list = new ArrayList();

       for(int i=0; i<10; i++){

           User user = new User();

           user.setUsername("User"+i);

           list.add(user);

       }

       ActionContext.getContext().put("users", list);

       User u = new User();

       u.setUsername("趙毅");

       ActionContext.getContext().getValueStack().push(u);

       return "detail";

    }
 

對應的JSP如下:

1:  <s:property value="username"/> <br/>

2:  <s:iterator value="#users">

3:     <s:property value="username"/>

4:     <s:property value="#root[2].username"/><br/>

5:  </s:iterator>

6:  <s:property value="username"/>

7:  <s:property value="#root[1].username"/> <!-- 張三 -->
 

根據剛才的示例,我們知道,第1行的username是“趙毅”(因為JSP在執行這行程式碼的時候,CompoundRoot中有兩個元素:第0個是“user物件趙毅”,第1個是“userAction物件張三”),因此第1行的username將取出CompoundRoot中第0個元素的username屬性:趙毅

第2行程式碼是iterator標籤,只定義了一個value屬性,iterator標籤將迴圈訪問users這個List中的User物件,並把當前迴圈的user物件壓入到CompoundRoot中!所以,在第3行和第4行程式碼被執行的時候,CompoundRoot中總共有3個元素:第0個元素是被iterator標籤壓入的當前迴圈的user物件;第1個元素是“user物件趙毅”;第2個元素是“userAction物件張三”,因此第3行程式碼的執行結果就是輸出“UserX”,即當前迴圈的user物件的username屬性!iterator標籤將會依次取出List中的user物件,並不斷壓入/彈出user物件(每次迴圈,都將執行一遍壓入/彈出)。而第4行程式碼取第2個元素的username屬性,即userAction物件的username屬性:張三。

第5行程式碼執行完成之後,在CompoundRoot中將剩下2個元素,與第2行程式碼被執行之前一樣。所以,第6行程式碼的輸出和第1行程式碼的輸出結果是一樣的,而第7行程式碼將取出userAction物件的username屬性:張三

本文來自CSDN部落格http://blog.csdn.net/li_tengfei/archive/2010/12/25/6098134.aspx

相關推薦

ValueStack的原理與生命周期

保存 RR serve nbsp array 順序 list 註意 檢索 1、ValueStack貫穿整個 Action 的生命周期,保存在request域中,所以ValueStack和request的生命周期一樣。當Struts2接受一個請求時,會迅速創建ActionCo

Struts2的ValueStack()的原理

1.     資料傳輸背後機制:ValueStack(值棧)   在這一切的背後,是因為有了ValueStack(值棧)! ValueStack基礎:OGNL 要了解ValueStack,必須先理解OGNL(Object Graphic Navigatino Lan

OGNL與

屬性 odin t對象 tac 結構 lang 引用 format 標簽庫 一、OGNL入門 1、什麽是OGNL   OGNL的全稱是對象圖導航語言(Object-Graph Navigation Language),它是一種功能強大的開源表達式語言。使用這種表達式語言,可

struts2學習(7)簡介與OGNL引入

ride tag per 集合 round del ram exception new 一、值棧簡介: 二、OGNL引入: com.cy.action.HelloAction.java: package com.cy.action; import java.util

(十)Struts2 /OGNL

下界 ads gre char void world 獲取值 efi expr 值棧是一組對象,按照提供的順序存儲以下這些對象: 序號對象和說明 1 Temporary對象 實際中存在各種在頁面執行期間創建的temporary對象。例如,JSP標簽循環集合的當前叠代值

獲取set()和push()方法向放的數據

[0 push nal lan padding har ade org odi ------------------siwuxie095 獲取 set() 方法向值棧放的數據 1、具體步驟

獲取數據

實現 space char row value ext throws tle 配置 --------------------siwuxie095 從值棧獲取數據 1、使用 Struts2 標簽 + OGNL 表

獲取對象

encoding entity ros padding 實現 conf apach end success -------------------siwuxie095 從值棧獲取對象 1、具體步驟 (1)

struts2存取數據小結(使用ognl表達式)

color idt 直接 com ble per 小結 如果 ges 前言:使用ognl和struts2標簽庫的時候要在 jsp頁面 中 加入這麽一行 <%@ taglib uri="/struts-tags" prefix="s" %> struts(統

【基於初學者的SSH】struts2 的詳解與struts2標簽庫+ognl表達式

radi ring etl action 值棧 多選 https submit 技術分享 一:什麽是值棧:struts2裏面本身提供的一種存儲機制,類似於域對象,值棧,可以存值和取值  特點:先進後出,最上面的元素叫做棧頂,也叫壓棧。  <s:debug><

Struts2 總結(ValueStack)

由於 壓入 system 因此 getc 進行 接口 上下文 通過 1、獲取值棧 1 //獲取值棧的第一種方式 2 ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest(

Struts2 (三) — OGNL與

運算 http 對象方法 4.2 ted pla put 翻譯 wrap 一、OGNL表達式 1.概述 1.1什麽是OGNL   ? OGNL是Object-Graph Navigation Language的縮寫,俗稱對象圖導航語言. 它是一種功能強大的表達式語言,通過它

hdu1237 表達式求--

tput desc std sta java case double 用例 bsp 簡單計算器 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total

C利用可變參數列表統計一組數的平均值,利用函數形式參數原理實現指針運算

alt 不同 因此 void 統計 int 順序 ret end //描述:利用可變參數列表統計一組數的平均值 #include <stdarg.h> #include <stdio.h> float average(int num, ...);

valueStack

通過 例如 就是 驅動 錯誤信息 wrap 非root jsp session 1. valueStack介紹我們使用valueStack的主要目的是為我將我們action中產生的數據攜帶到頁面上,也就是說valueStack它就是一個容器。在Struts2框架中將valu

NOIP 2011 普及組 T4 表達式的

字符串包含 pri 進制 字符 bits range 一個 DC 一個棧 題目 題目描述 對於1 位二進制變量定義兩種運算: 運算的優先級是: 先計算括號內的,再計算括號外的。 “× ”運算優先於“⊕”運算,即計算表達式時,先計算× 運算,再計算⊕運算。例如:計

基於數值分析思想對多項式求原理和應用進行探究

數值分析 use com 相加 emp 要花 class RoCE size 摘要:多項式是由多個單項(符號項如:5x或者常數項4)通過四則運算組合起來的式子,如P(x)=2x^4+3x^3-3x^2+5x-1 一般的求解會將特定的x代入到上式中,一個一個的計算,共需要

三、StrutsOGNL表達式與視頻

很多 play 視頻 表達 left mage com image right 1、OGNL 1.1、OGZL 概述   OGNL:對象圖導航語言,比 EL 表達式強大很多倍的語言。     EL:從域對象中獲取數據,從 EL 的11和對象中獲取。三、StrutsOGNL

struts2命名的一個bug

turn 如果 class ima 應該 方法 src inf code Action中的的屬性名和值棧中屬性名應該數絕對對應的關系,但是如果引用名和get方法寫成如下形式: private List<User> uList; public List

Ognl和

Ognl OGNL是Xwork框架提供的功能,Xwork是struts2基礎架構,Xwork是一個ioc容器。 OGNL是Object Graphic Navigation Language(物件圖導航語言)的縮寫,它是一個開源專案,struts2使用ognl,作為表示式語言。 使用OG