Struts2中的ValueStack值棧對象詳解

 

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

 

ValueStack基礎:OGNL

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

OGNLStruts2中使用的一種表達式語言,它可以用於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方法),導航到OrganizationorgId屬性,如下所示:

 

//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(王五);

       //調用靜態變量

       //注意:outSystem中的靜態變量,outPrintStream類型的一個對象

       //println()則是out這個對象中的實例方法(不是靜態方法)

       //調用靜態方法,需要在類名和變量名前面加上@來調用,對於實例方法,用“.”來調用

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

    }

   

    public void testOgnl14() throws Exception{

       User user = new User();

       user.setUsername(“wangwu”);

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

        Ognl.getValue(“@[email protected](@cn.com.leadfar.utils.Utils@toUpperCase(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是一個接口,而OgnlValueStackstrtus2中的缺省實現。ValueStack中的數據,分兩個部分存放:rootcontext(這與OGNL中的概念一致),同時ValueStack暴露相關的接口:

void setValue(String expr, Object value);

Object findValue(String expr);

用來通過OGNL表達式對ValueStack中的數據進行操作!

 

ValueStack中的root對象是CompoundRootCompoundRoot繼承了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(即調用ValueStacksetValuefindValue方法),而表達式中包含有對root對象的訪問操作,ValueStack將依次從棧頂往棧底搜索CompoundRoot對象中所包含的對象,看哪個對象具有相應的屬性,找到之後,立刻返回。

 

Struts2中,一個請求在最終到達Action的方法之前,Action對象本身會被壓入ValueStack(實際上就是放到ValueStackCompoundRoot中),所以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中,給Actionusername/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()方法,將繼續搜索第123……個元素是否有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中,這時候,在ValueStackCompoundRoot中將有兩個元素:第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屬性:張三

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章