OGNL & ValueStack 入門

 h1. 一個例子
請看下面的需求,假設有如下用戶對象模型:
Java代碼
public interface User {   
    public String getName();   
    public Date getRegisterDate();   
    public Customer getCustomer();   
}   
public interface Customer {   
    public String getId();   
    public String getName();   
    public boolean isVip();   
}   
public interface EntCustomer extends Customer {   
    public String getTrustId(); // 組織機構代碼證號   
}  

對於給定的用戶jack,且該用戶所屬客戶是企業客戶,那麼我們如何獲取該用戶的姓名?如何獲取用戶所屬客戶的名稱?如何判斷該用戶所屬客戶是否是VIP客戶?如何取jack所屬企業的組織機構代碼證號?

* 採用java代碼的方式,我們可以用如下的API調用得到所需信息:
Java代碼
jack.getName();   
jack.getCustomer().getName();   
jack.getCustomer().isVip();   
((EntCustomer)jack.getCustomer()).getTrustId();  


* 但我們現在在講述OGNL,因此通過採用OGNL,我們可以用如下方式取得我們所需要的信息:
Java代碼
jack.name   
jack.customer.name   
jack.vip   
jack.customer.trustId  

由此我們可以看到OGNL的表達方式與java表達方式有以下幾點不同:
** 不需關注對象類型,不需進行類型轉換
** 表達方式更簡短和直觀

OGNL表達式最大的優點就是:*簡單* 和 *直觀*,你不這樣認爲嗎? 如果你覺得上面的表達式還不夠簡單和直觀,那我們再來看:
Java代碼
name  
這也是一個OGNL表達式,也就是取姓名!簡單嗎?至少足夠直觀了吧

h1. 基本概念
我們前面看到了OGNL的一個最簡單的例子,事實上OGNL確實很簡單,如果能理解上面那個例子的用法,那麼我們就掌握了OGNL的80%的用法了。
上面的例子雖然簡單,但其中卻含有OGNL的兩個最基本的概念:*表達式(expression)* 和 *上下文(context)*,我們先看*表達式*。

h2. 表達式
OGNL就是表達式!它能讓我們用簡潔直觀的語法表達我們的想法,如同上面的例子一般。簡潔直觀就是表達式的最大優點!我們知道表達式總是有一個結果,也就是說表達式總是會求值出一個結果,這個結果可能是一個字符串(如名稱、組織機構代碼證號等),或者是一個布爾值(如是否是VIP客戶等),至於這個結果要怎麼使用,那就是我們自己來決定的了。

h3. 上下文(context)
表達式的概念,我相信很好理解,但什麼是上下文(context)?簡單來說上下文就是環境,表達式求值的環境!還是不理解嗎?我們來看一個例子:
還是上面最後那個例子:Java代碼
name  

細心的你是否會問,這個表達式要取誰的姓名呢?OK,很好!這就是環境,"誰"就存在於環境之中,也就是存在上下文之中。對於不同的環境/上下文,相同的表達式會有不同的結果!而環境/上下文的實質是什麼呢?就是一組帶名稱的對象集合。

引用
思考:表達環境或上下文這個概念的最好的數據結構是什麼?


h4. OGNL上下文概念詳解
我們前面說上下文就是一組名稱-對象對的集合,如下圖所示就是一個簡單的上下文:
Java代碼
user ---> User(name:"jack", ...)   
request ---> HttpServletRequest(header: ...)  

那麼在上面的環境中,我們可以有如下的OGNL表達式:
Java代碼
#user.name // 取用戶的姓名   
#user.age // 取用戶年齡   
#user.birthday // 取用戶生日   
#user.customer.name // 取用戶所屬客戶的名稱   
#request.parameters // 取請求參數  

請注意上面表達式中的"#user"和"#request"的用法,"#"表示訪問環境/上下文中的對象。

現在可以很方便地訪問環境中的對象了,那麼如果你比較懶惰的話(記住:在程序員羣體,懶惰是褒義詞!),你是否覺得訪問用戶的姓名,年齡,生日,等等其它屬性如果全部要使用"#user"來訪問會不會太麻煩了呢?OK,ONGL的設計者早就考慮了這個問題,我們可以指定user爲環境中的特權對象,訪問該對象可以不需要使用#user的方式,如下所示代碼與上面的完全等價,當然,前提是要預先指定user爲特權對象:
Java代碼
name // 取用戶的姓名   
age // 取用戶年齡   
birthday // 取用戶生日   
customer.name // 取用戶所屬客戶的名稱   
#request.parameters // 取請求參數  

我們上面所說的"特權對象"在OGNL中稱爲"根對象"(root)

h5. 小結
綜上所述,理解OGNL表達式的關鍵是理解其上下文的概念,因爲OGNL的上下文概念中引入了"根對象"的概念,所以初學者往往會在這裏迷失方向。


引用

OGNL的中文全稱是對象圖導航語言,也就是說OGNL是一門語言,如同java是一門語言一樣。你是否會認爲OGNL的作者太誇張了,竟敢把表達式謊稱爲語言?不,OGNL的語法確實非常簡潔,OGNL的代碼(我沒有說表達式,因爲代碼是和語法相匹配的詞語)通常不會換行,這意味着我們不可能把OGNL的代碼寫得很長,但是,這並不意味着OGNL的表達能力很弱。事實上,OGNL的語法設計非常簡潔,但其功能卻相當強大,如果你有興趣,可以深入閱讀OGNL參考手冊的集合與lambda章節。

慢着,事情還未至此結束!struts2對OGNL中的上下文的概念又定義了新的含義,且聽我慢慢道來!

h3. struts2中的OGNL上下文
struts2對OGNL上下文的概念又做了進一步擴充,在struts2中,OGNL上下文通常如下所示:
Java代碼
|   
                     |--request   
                     |   
                     |--application   
                     |   
       context map---|--OgnlValueStack(root) [ user, action, OgnlUtil, ... ]   
                     |   
                     |--session   
                     |   
                     |--attr   
                     |   
                     |--parameters  

我們可以使用"#requet"訪問HttpServletRequest對象, "#session"訪問HttpSession對象,但請注意"根對象"是什麼?是ValueStack!
那麼ValueStack是什麼?值棧。也就是一組對象的堆棧。也就是說,在struts2中,根對象不是我們通常的一個對象,而是一組對象。我們可以push新的對象到值棧中,也可以彈出值棧的棧頂對象。如上圖所示,假設我們將user對象push到值棧中,那麼如下的表達式將與之前我們見過的表達式一樣,具有相同的結果:
Java代碼
name // 取用戶的姓名   
age // 取用戶年齡   
birthday // 取用戶生日   
customer.name // 取用戶所屬客戶的名稱   
#request.parameters // 取請求參數  

也就是說,我們使用name這個表達式的時候,ONGL會取"根對象"的name屬性,但現在根對象是ValueStack!那麼訪問ValueStack的name屬性意味着什麼呢?這意味着: ValueStack會先查看棧頂元素是否有name屬性,如果有就返回該屬性值,否則取出棧頂下的元素,繼續查看,直到棧底爲止。

以上就是OGNL表達式的核心概念,你理解了嗎?下一步,你需要了進一步瞭解OGNL的語法,以發掘其更強大的功能!

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