SSH框架中鮮爲人知的細節(二)

Struts2之ValueStack

    上一節在將闡述ModelDriven的機制時,經常提到一個名詞ValueStack。也許你會堅決果斷脫口而出,不就是值棧嗎?對,就是它,那你知道Struts爲何須要引入它?它是如何工做的?它和OGNL有何私情?若是你對以上問題的答案很模糊,可是又確實想知道答案,那麼本文將帶你去看看ValueStack的世界。

html

Why ValueStack?

    說Struts就不得不提MVC,全部的請求都是基於頁面(View)提交,頁面是數據最初的載體,而後提交給Controller,由Controller將數據作第一次處理,而後交給業務層作進一步的處理。下面,咱們仍是經過代碼直觀的體會一下。java

JSP頁面僞碼:數據結構

<form action="xxx/user-add.action" method="post">
    username:<input type="text" name="username" />
    age:<input type="text" name="age" />
    groupName:<input type="text" name="group.name" />
    <input type="submit" name="submit" value="添加" />
</form>

VO僞碼:ide

public class UserVO {
    private String username;
    private int age;
    private Group group;

    //getter and setter
    //...
}

public class Group {
    private String name;
    
    //getter and setter
    //...
}

Action僞碼:post

public class UserAction implements ModelDriven {

    private User user = new User();

    public String addUser() {
        //相應的業務邏輯
    }

    public String queryUser() {
        //返回user對象
    }

    @Override
    public Object getModel() {
        return user;
    }
}

    經過以上代碼,能夠看出來, 數據在View層是扁平的,沒有數據類型之分,均爲字符串。可是在Java中,數據有各類數據類型(age -> int),豐富的數據結構 (group -> Group)。因此數據由頁面向Java流轉時,就會出現類型如何匹配的問題。
spa

    爲了解決以上的問題,Struts2採納了XWork的OGNL(後面會專門講解)作爲解決方案,在OGNL基礎上構建了OGNLValueStack機制,完美的實現了數據由View到Controller的匹配問題code


ValueStack如何運轉?

    你們必定對下面這段代碼不陌生:orm

username:<s:property value="username" />
age:<s:property value="age" />
groupName:<s:property value="group.name" />

    使用Struts2的Tag經過OGNL取值,在上一節咱們講到,user對象最終被保存到了ValueStack中。若是你有興趣閱讀struts2的源碼時,你會發現ValueStack只是一個接口,而Struts2使用了OgnlValueStack做爲其默認實現。htm

ValueStack接口中聲明瞭getRoot()這樣一個接口:對象

/**
 * Get the CompoundRoot which holds the objects pushed onto the stack
 *
 * @return the root
 */
public abstract CompoundRoot getRoot();

看下CompoundRoot的實現:

public class CompoundRoot extends ArrayList {

    //...省略一些其餘實現
    
    public Object peek() {
        return get(0);
    }

    public Object pop() {
        return remove(0);
    }

    public void push(Object o) {
        add(0, o);
    }
}

    學過數據結構的一眼就看出來,CompoundRoot具備棧的特性:先進後出。因此在s:property標籤中的OGNL表達式,最終會交給ValueStack來解析。username就是一個OGNL表達式,意思是調用root對象的getUsername()方法。Struts2將自動搜索CompoundRoot中有哪些元素(從第0個元素開始搜索),檢測這些元素是否有getUsername()方法,若是第0個元素沒有getUsername()方法,將繼續搜索第一、二、3……個元素是否有getUsername()方法。很明顯,user對象有這個方法,因此就能夠顯示username的值。既然CompoundRoot維護的是一個棧結構,那麼若是有多個元素都含有getUsername()方法的話,那麼<s:property value="username" />只會獲得最後一個壓入棧的元素對應的username的值。

    

ValueStack & OGNL

    在講ValueStack的過程當中,OGNL一直伴隨左右,那麼什麼是OGNL呢?官方的解釋就是:Object Graph Navigation Language,是一種表達式語言。使用這種表達式語言,你能夠經過某種表達式語法,存取Java對象樹中的任意屬性、調用Java對象樹的方法、同時可以自動實現必要的類型轉化。若是咱們把表達式看作是一個帶有語義的字符串,那麼OGNL無疑成爲了這個語義字符串與Java對象之間溝通的橋樑。下面經過一個簡單的程序看看OGNL的用法。

public class OgnlTest {
    
    @Test
    public void test01() {

        //建立root對象
        User u = new User();
        u.setUsername("javer");
        u.setAge(18);
        Group g = new Group();
        g.setName("group01");
        u.setGroup(g);
        
        //建立context
        Map ctx = new HashMap();
        ctx.put("context", "get from context:");
        
        //直接取root對象
        Object username = Ognl.getValue(Ognl.parseExpression("username"), u);
        AssertEquals("javer", username); //true

        //從上下文取值
        Object value = Ognl.getValue(Ognl.parseExpression("#context", ctx, u):
        AssertEquals("get from context:", value); //true

        //從上下文取root對象
        Object value = Ognl.getValue(Ognl.parseExpression("#context + username", ctx, u);
        AssertEquals("get from context:javer", value);
    }
}

    上面這個例子其實涵蓋了OGNL的三大要點:

1. Expression:整個OGNL的核心,全部的OGNL操做都是針對表達式的解析後進行的。表達式會規定這次OGNL操做到底要幹什麼

2. Root Object:OGNL的操做對象,即指定到底「對誰幹」

3. Context:OGNL的內部,全部的操做都會在一個特定的環境中運行,這個環境就是OGNL的上下文環境,將規定OGNL的操做「在哪裏幹」

    回過頭來再看看ValueStack,值棧中的數據,也是分兩個部分存放:root和context。若是某個OGNL表達式被傳遞給ValueStack(即調用ValueStack的setValue或findValue方法),而表達式中包含有對root對象的訪問操做,ValueStack將依次從棧頂往棧底搜索CompoundRoot對象中所包含的對象,看哪一個對象具備相應的屬性,找到以後,馬上返回。

    因此ValueStack就是對OGNL進行了封裝,加強了部分功能。說白了,ValueStack其實就是OGNL的一個擴展,在OGNL只支持一個root的基礎上擴展到了支持多個root對象。說到這裏,可能你也恍然 大悟,Action就是一個root對象,在不使用ModelDriven的時候,頁面上的元素不就是和Action對象中的成員變量一一對應嗎!這下兩者的關係你心中也有數了吧。

相關文章
相關標籤/搜索