h1. 一個(gè)例子
請看下面的需求,假設有如下用戶(hù)對象模型:
- 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();
- }
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(); // 組織機構代碼證號}對于給定的用戶(hù)jack,且該用戶(hù)所屬客戶(hù)是企業(yè)客戶(hù),那么我們如何獲取該用戶(hù)的姓名?如何獲取用戶(hù)所屬客戶(hù)的名稱(chēng)?如何判斷該用戶(hù)所屬客戶(hù)是否是VIP客戶(hù)?如何取jack所屬企業(yè)的組織機構代碼證號?
* 采用java代碼的方式,我們可以用如下的API調用得到所需信息:
- jack.getName();
- jack.getCustomer().getName();
- jack.getCustomer().isVip();
- ((EntCustomer)jack.getCustomer()).getTrustId();
jack.getName();jack.getCustomer().getName();jack.getCustomer().isVip();((EntCustomer)jack.getCustomer()).getTrustId();
* 但我們現在在講述OGNL,因此通過(guò)采用OGNL,我們可以用如下方式取得我們所需要的信息:
- jack.name
- jack.customer.name
- jack.vip
- jack.customer.trustId
jack.namejack.customer.namejack.vipjack.customer.trustId
由此我們可以看到OGNL的表達方式與java表達方式有以下幾點(diǎn)不同:
** 不需關(guān)注對象類(lèi)型,不需進(jìn)行類(lèi)型轉換
** 表達方式更簡(jiǎn)短和直觀(guān)
OGNL表達式最大的優(yōu)點(diǎn)就是:*簡(jiǎn)單* 和 *直觀(guān)*,你不這樣認為嗎? 如果你覺(jué)得上面的表達式還不夠簡(jiǎn)單和直觀(guān),那我們再來(lái)看:
name
這也是一個(gè)OGNL表達式,也就是取姓名!簡(jiǎn)單嗎?至少足夠直觀(guān)了吧:)
h1. 基本概念
我們前面看到了OGNL的一個(gè)最簡(jiǎn)單的例子,事實(shí)上OGNL確實(shí)很簡(jiǎn)單,如果能理解上面那個(gè)例子的用法,那么我們就掌握了OGNL的80%的用法了。
上面的例子雖然簡(jiǎn)單,但其中卻含有OGNL的兩個(gè)最基本的概念:*表達式(expression)* 和 *上下文(context)*,我們先看*表達式*。
h3. 表達式
OGNL就是表達式!它能讓我們用簡(jiǎn)潔直觀(guān)的語(yǔ)法表達我們的想法,如同上面的例子一般。簡(jiǎn)潔直觀(guān)就是表達式的最大優(yōu)點(diǎn)!我們知道表達式總是有一個(gè)結果,也就是說(shuō)表達式總是會(huì )求值出一個(gè)結果,這個(gè)結果可能是一個(gè)字符串(如名稱(chēng)、組織機構代碼證號等),或者是一個(gè)布爾值(如是否是VIP客戶(hù)等),至于這個(gè)結果要怎么使用,那就是我們自己來(lái)決定的了。
h3. 上下文(context)
表達式的概念,我相信很好理解,但什么是上下文(context)?簡(jiǎn)單來(lái)說(shuō)上下文就是環(huán)境,表達式求值的環(huán)境!還是不理解嗎?我們來(lái)看一個(gè)例子:
還是上面最后那個(gè)例子:
name
細心的你是否會(huì )問(wèn),這個(gè)表達式要取誰(shuí)的姓名呢?OK,很好!這就是環(huán)境,"誰(shuí)"就存在于環(huán)境之中,也就是存在上下文之中。對于不同的環(huán)境/上下文,相同的表達式會(huì )有不同的結果!而環(huán)境/上下文的實(shí)質(zhì)是什么呢?就是一組帶名稱(chēng)的對象集合。
引用
思考:表達環(huán)境或上下文這個(gè)概念的最好的數據結構是什么?
h3. OGNL上下文概念詳解
我們前面說(shuō)上下文就是一組名稱(chēng)-對象對的集合,如下圖所示就是一個(gè)簡(jiǎn)單的上下文:
- user ---> User(name:"jack", ...)
- request ---> HttpServletRequest(header: ...)
user ---> User(name:"jack", ...)request ---> HttpServletRequest(header: ...)
那么在上面的環(huán)境中,我們可以有如下的OGNL表達式:
- #user.name
- #user.age
- #user.birthday
- #user.customer.name
- #request.parameters
#user.name // 取用戶(hù)的姓名#user.age // 取用戶(hù)年齡#user.birthday // 取用戶(hù)生日#user.customer.name // 取用戶(hù)所屬客戶(hù)的名稱(chēng)#request.parameters // 取請求參數
請注意上面表達式中的"#user"和"#request"的用法,"#"表示訪(fǎng)問(wèn)環(huán)境/上下文中的對象。
現在可以很方便地訪(fǎng)問(wèn)環(huán)境中的對象了,那么如果你比較懶惰的話(huà)(記?。涸诔绦騿T群體,懶惰是褒義詞?。?,你是否覺(jué)得訪(fǎng)問(wèn)用戶(hù)的姓名,年齡,生日,等等其它屬性如果全部要使用"#user"來(lái)訪(fǎng)問(wèn)會(huì )不會(huì )太麻煩了呢?OK,ONGL的設計者早就考慮了這個(gè)問(wèn)題,我們可以指定user為環(huán)境中的特權對象,訪(fǎng)問(wèn)該對象可以不需要使用#user的方式,如下所示代碼與上面的完全等價(jià),當然,前提是要預先指定user為特權對象:
- name
- age
- birthday
- customer.name
- #request.parameters
name // 取用戶(hù)的姓名age // 取用戶(hù)年齡birthday // 取用戶(hù)生日customer.name // 取用戶(hù)所屬客戶(hù)的名稱(chēng)#request.parameters // 取請求參數
我們上面所說(shuō)的"特權對象"在OGNL中稱(chēng)為"根對象"(root)
h3. 小結
綜上所述,理解OGNL表達式的關(guān)鍵是理解其上下文的概念,因為OGNL的上下文概念中引入了"根對象"的概念,所以初學(xué)者往往會(huì )在這里迷失方向。
引用
OGNL的中文全稱(chēng)是對象圖導航語(yǔ)言,也就是說(shuō)OGNL是一門(mén)語(yǔ)言,如同java是一門(mén)語(yǔ)言一樣。你是否會(huì )認為OGNL的作者太夸張了,竟敢把表達式謊稱(chēng)為語(yǔ)言?不,OGNL的語(yǔ)法確實(shí)非常簡(jiǎn)潔,OGNL的代碼(我沒(méi)有說(shuō)表達式,因為代碼是和語(yǔ)法相匹配的詞語(yǔ))通常不會(huì )換行,這意味著(zhù)我們不可能把OGNL的代碼寫(xiě)得很長(cháng),但是,這并不意味著(zhù)OGNL的表達能力很弱。事實(shí)上,OGNL的語(yǔ)法設計非常簡(jiǎn)潔,但其功能卻相當強大,如果你有興趣,可以深入閱讀OGNL參考手冊的集合與lambda章節。
慢著(zhù),事情還未至此結束!struts2對OGNL中的上下文的概念又定義了新的含義,且聽(tīng)我慢慢道來(lái)!
h3. struts2中的OGNL上下文
struts2對OGNL上下文的概念又做了進(jìn)一步擴充,在struts2中,OGNL上下文通常如下所示:
- |
- |--request
- |
- |--application
- |
- context map---|--OgnlValueStack(root) [ user, action, OgnlUtil, ... ]
- |
- |--session
- |
- |--attr
- |
- |--parameters
||--request||--application|context map---|--OgnlValueStack(root) [ user, action, OgnlUtil, ... ]||--session||--attr||--parameters
我們可以使用"#requet"訪(fǎng)問(wèn)HttpServletRequest對象, "#session"訪(fǎng)問(wèn)HttpSession對象,但請注意"根對象"是什么?是ValueStack!
那么ValueStack是什么?值棧。也就是一組對象的堆棧。也就是說(shuō),在struts2中,根對象不是我們通常的一個(gè)對象,而是一組對象。我們可以push新的對象到值棧中,也可以彈出值棧的棧頂對象。如上圖所示,假設我們將user對象push到值棧中,那么如下的表達式將與之前我們見(jiàn)過(guò)的表達式一樣,具有相同的結果:
- name
- age
- birthday
- customer.name
- #request.parameters
name // 取用戶(hù)的姓名age // 取用戶(hù)年齡birthday // 取用戶(hù)生日customer.name // 取用戶(hù)所屬客戶(hù)的名稱(chēng)#request.parameters // 取請求參數
也就是說(shuō),我們使用name這個(gè)表達式的時(shí)候,ONGL會(huì )取"根對象"的name屬性,但現在根對象是ValueStack!那么訪(fǎng)問(wèn)ValueStack的name屬性意味著(zhù)什么呢?這意味著(zhù): ValueStack會(huì )先查看棧頂元素是否有name屬性,如果有就返回該屬性值,否則取出棧頂下的元素,繼續查看,直到棧底為止。
以上就是OGNL表達式的核心概念,你理解了嗎?下一步,你需要了進(jìn)一步了解OGNL的語(yǔ)法,以發(fā)掘其更強大的功能!