本文包括:
1摩幔、OGNL 表達式概述(了解)
2、值棧概述
3铐尚、值棧的存值與取值
4拨脉、EL 表達式也會獲取到值棧中的數(shù)據(jù)
5、總結(jié) OGNL 表達式的特殊的符號
1宣增、OGNL 表達式概述(了解)
-
OGNL 是 Object Graphic Navigation Language(對象圖導(dǎo)航語言)的縮寫
所謂對象圖玫膀,即以任意一個對象為根,通過 OGNL 可以訪問與這個對象關(guān)聯(lián)的其它對象
通過它簡單一致的表達式語法爹脾,可以存取對象的任意屬性帖旨,調(diào)用對象的方法,遍歷整個對象的結(jié)構(gòu)圖灵妨,實現(xiàn)字段類型轉(zhuǎn)化等功能解阅。它使用相同的表達式去存取對象的屬性
-
Struts2 框架使用 OGNL 作為默認的表達式語言
OGNL 是一種比 EL 強大很多倍的語言
xwork 提供 OGNL表達式
ognl-3.0.5.jar
-
OGNL 提供五大類功能
支持對象方法調(diào)用
支持類靜態(tài)的方法調(diào)用和值訪問
訪問 OGNL 上下文(OGNL context)和 ActionContext
支持賦值操作和表達式串聯(lián)
操作集合對象
-
測試的代碼
// 訪問對象的方法 @Test public void run1() throws OgnlException{ OgnlContext context = new OgnlContext(); // 獲取對象的方法 Object obj = Ognl.getValue("'helloworld'.length()", context, context.getRoot()); System.out.println(obj); } // 獲取 OGNL 上下文件的對象 @Test public void run3() throws OgnlException{ OgnlContext context = new OgnlContext(); context.put("name", "美美"); Object obj = Ognl.getValue("#name", context, context.getRoot()); System.out.println(obj); } // 從 root 棧獲取值 @Test public void demo3() throws OgnlException{ OgnlContext context = new OgnlContext(); Customer c = new Customer(); c.setCust_name("haha"); context.setRoot(c); String name = (String) Ognl.getValue("cust_name", context, context.getRoot()); System.out.println(name); }
JSP 頁面中使用 OGNL 表達式
Struts2 引入了 OGNL 表達式,主要是在 JSP 頁面中獲取值棧中的值
-
具體在 Struts2 中怎么使用呢泌霍?如下步驟:
-
需要先引入 Struts2 的標(biāo)簽庫
<%@ taglib prefix="s" uri="/struts-tags" %>
-
使用 Struts2 提供的標(biāo)簽中的標(biāo)簽货抄,如下的 property 標(biāo)簽會從值棧中取值
<s:property value="OGNL表達式"/>
-
-
在 JSP 頁面使用 OGNL 表達式
-
訪問對象方法
<s:property value="'hello'.length()"/>
網(wǎng)頁會輸出5,因為 'hello' 的長度為5
-
2朱转、值棧概述
-
什么是值棧蟹地?
值棧就相當(dāng)于 Struts2 框架的數(shù)據(jù)的中轉(zhuǎn)站,可以向值棧存入一些數(shù)據(jù)藤为,也可以從值棧中獲取到數(shù)據(jù)怪与。
ValueStack 是 struts2 提供一個接口,它有個實現(xiàn)類 OgnlValueStack ---- 值棧對象 (OGNL 是從值棧中獲取數(shù)據(jù)的)
Action 是多例的缅疟,有一個請求分别,就會創(chuàng)建 Action 實例,然后創(chuàng)建一個 ActionContext 對象窿吩,代表的是 Action 的上下文對象茎杂,同時還會創(chuàng)建一個 ValueStack 對象。 每個 Action 實例都有一個 ValueStack 對象 (一個請求對應(yīng)一個 ValueStack 對象 )纫雁,在其中保存當(dāng)前 Action 對象和其他相關(guān)對象
-
Struts2 框架把 ValueStack 對象保存在名為 “struts.valueStack” 的請求屬性中煌往,request 中(值棧對象是 request 的一個屬性)
ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack");
-
值棧的內(nèi)部結(jié)構(gòu) ?
-
值棧由兩部分組成
root -- Struts2 把動作和相關(guān)對象壓入 ObjectStack 中--List
context -- Struts2 把各種各樣的映射關(guān)系(一些 Map 類型的對象) 壓入 ContextMap 中
-
Struts2 會默認把下面這些映射壓入 Context(即壓入 Map)中
-
注意:request 代表的是 Map 集合的 key 值(實質(zhì)就是一個字符串),value 的值其實也是一個 Map 集合刽脖。
parameters: 該 Map 中包含當(dāng)前請求的請求參數(shù) ?name=xxx&password=123
request: 該 Map 中包含當(dāng)前 request 對象中的所有屬性
session: 該 Map 中包含當(dāng)前 session 對象中的所有屬性
application:該 Map 中包含當(dāng)前 application 對象中的所有屬性
attr: 該 Map 按如下順序來檢索某個屬性: request, session, application
-
-
ValueStack 中存在 root 屬性 (實質(zhì)是 CompoundRoot 類型) 羞海、 context 屬性 (實質(zhì)是 OgnlContext 類型)
CompoundRoot 就是 ArrayList(因為繼承 ArrayList)
OgnlContext 就是 Map(因為繼承 Map)
-
context 對應(yīng) Map 引入 root 對象
context 中還存在 request、session曲管、application却邓、attr、parameters 對象引用
-
OGNL 表達式訪問值棧中的數(shù)據(jù)
訪問 root 中數(shù)據(jù)時不需要 #
訪問 request院水、session腊徙、application、attr檬某、parameters 對象數(shù)據(jù)時必須寫 #
操作值棧默認操作 root 元素
-
-
-
值棧的創(chuàng)建和 ActionContext 對象的關(guān)系(從源代碼分析)
值棧對象是請求時創(chuàng)建的
ActionContext 是綁定到當(dāng)前的線程上撬腾,那么在每個攔截器或者 Action 中獲取到的 ActionContext 是同一個(ThreadLocal ,線程安全)
ActionContext 中存在一個 Map 集合恢恼,該 Map 集合和 ValueStack 的 context 是同一個地址民傻。
-
ActionContext 中可以獲取到 ValueStack 的引用,所以以后不用 request 來得到 ValueStack 對象场斑,都使用 ActionContext 來獲取到值棧對象
ValueStack vs = ActionContext.getContext().getValueStack();
3漓踢、值棧的存值與取值
-
向值棧保存數(shù)據(jù) (主要針對 root 棧)
-
push 方法:底層調(diào)用 root 對象的 push 方法(把元素添加到 0 位置)
valueStack.push(Object obj);
root 對象繼承 ArrayList,再往底層研究漏隐,發(fā)現(xiàn)其實調(diào)用了 ArrayList 的 add 方法喧半,且把元素添加到了0的位置,0就是棧頂锁保。
-
set 方法:底層獲取一個 map 集合(該 map 有可能是已經(jīng)存在的薯酝,有可能是新創(chuàng)建的)半沽,把 map 集合 push 到棧頂爽柒,再把數(shù)據(jù)存入到該 map 集合中。
valueStack.set(String key, Object obj);
在jsp中 通過 <s:debug /> 查看值棧的內(nèi)容
-
-
從值棧中獲取值
-
在 JSP 中獲取值棧的數(shù)據(jù)
-
總結(jié)幾個小問題:
訪問 root 中數(shù)據(jù) 不需要#
訪問 context 其它對象數(shù)據(jù) 加 #
如果向 root 中存入對象的話者填,優(yōu)先使用 push 方法。
如果向 root 中存入集合的話,優(yōu)先要使用 set 方法白嘁。
-
在 Context 中獲取數(shù)據(jù)(context 棧)
在Action中向域?qū)ο笾写嫒胫?/p>
-
request:
<s:property value="#request.username"/>
-
session:
<s:property value="#session.username"/>
-
application:
<s:property value="#application.username"/>
-
attr:
<s:property value="#attr.username"/>
-
parameters:
<s:property value="#parameters.cid"/>
-
-
存取示例代碼如下(root 棧)
-
demo1:
vs.push("美美"); <s:property value="[0].top"/>
-
demo2:
// 棧頂是map集合翩迈,通過key獲取值 vs.set("msg", "小鳳"); <s:property value="[0].top.msg"/>
-
demo3:
// 棧頂放user對象 vs.push(user); <s:property value="[0].top.username"/> <s:property value="[0].top.password"/> // [0].top 關(guān)鍵字是可以省略的,如下也是可行的 <s:property value="username"/>
-
demo4:
vs.set("user", user); <s:property value="[0].top.user.username"/> <s:property value="[0].top.user.password"/> // 省略關(guān)鍵字 <s:property value="user.username"/>
-
demo5:
// 若在ValueStack1Action中提供了成員的屬性榨乎,Action進棧怎燥,則“小澤”也會入棧 private User user = new User("小澤","456"); public User getUser() { return user; } public void setUser(User user) { this.user = user; } // 在excute方法中再壓入“小蒼” User user = new User("小蒼","123"); vs.set("user", user); // 從棧頂開始查找,找user的屬性username屬性蜜暑,因為省略了序號铐姚,所以默認是[0].top,又小蒼后壓入肛捍,應(yīng)該返回小蒼 <s:property value="user.username"/> // [1].top獲取ValueStack1Action // [1].top.user返回user對象 即“小澤”對象 // [1].top.user.username獲取對象的屬性名稱隐绵,即小澤 <s:property value="[1].top.user.username"/>
-
demo6:
//棧頂是list集合 vs.push(ulist); <s:property value="[0].top[0].username"/> <s:property value="[0].top[1].username"/>
-
demo7:
vs.set("ulist", ulist); <s:property value="ulist[0].username"/>
-
demo8:
屬性 * value 要迭代的集合之众,需要從值棧中獲取 * var 迭代過程中,遍歷的對象 * var有依许,把迭代產(chǎn)生的對象默認壓入到context棧中棺禾,從context棧取值,加#號 * var無峭跳,默認把迭代產(chǎn)生的對象壓入到root棧中 // 編寫var的屬性 <s:iterator value="ulist" var="u"> <s:property value="#u.username"/> <s:property value="#u.password"/> </s:iterator> // 沒有編寫var關(guān)鍵字 <s:iterator value="ulist"> <s:property value="username"/> <s:property value="password"/> </s:iterator>
-
demo9:
//從context棧中獲取值膘婶,加#號 HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("msg", "美美"); request.getSession().setAttribute("msg", "小風(fēng)"); <s:property value="#request.msg"/> <s:property value="#session.msg"/> <s:property value="#parameters.id"/> <s:property value="#attr.msg"/>
-
demo10:
<!-- 在JSP頁面上,查看值棧的內(nèi)部結(jié)構(gòu) --> <s:debug></s:debug>
-
-
4蛀醉、EL表達式也會獲取到值棧中的數(shù)據(jù)
-
為什么EL也能訪問值棧中的數(shù)據(jù)竣付?
-
StrutsPreparedAndExecuteFilter 的 doFilter() 方法代碼中
request = prepare.wrapRequest(request);
對 Request 對象進行了包裝 ,StrutsRequestWrapper
增強了request的 getAttribute() 方法:Object attribute = super.getAttribute(s); if (attribute == null) { attribute = stack.findValue(s); }
訪問 request 范圍的數(shù)據(jù)時滞欠,如果數(shù)據(jù)找不到古胆,去值棧中找
request 對象,所以具備訪問值棧數(shù)據(jù)的能力(查找 root 的數(shù)據(jù))筛璧。
-
5逸绎、總結(jié) OGNL 表達式的特殊的符號
-
#
符號的用法-
獲得 contextMap 中的數(shù)據(jù)
<s:property value="#request.name"/> <s:property value="#session.name"/> <s:property value="#application.name"/> <s:property value="#attr.name"/> <s:property value="#parameters.id"/> <s:property value="#parameters.name"/>
-
構(gòu)建一個map集合
-
普通表單
<form action="" method="post"> 性別:<input type="radio" name="sex" value="1"/>男<input type="radio" name="sex" value="2"/>女 </form>
-
使用 OGNL 標(biāo)簽
<s:form action="" method="post"> 性別:<s:radio name="sex" list="{'男','女'}"/> </s:form>
注意:這樣的寫法,若選擇男夭谤,則 sex='男'
若像下面這種寫法棺牧,若選擇男,則 sex='0'
<s:radio name="sex" list="#{'0':'男','1':'女'}"></s:radio>
-
-
-
%
符號的用法-
強制字符串解析成 OGNL 表達式朗儒。
例如:在 request 域中存入值颊乘,然后在文本框(
<s:textfield>
)中取值,現(xiàn)在到value上醉锄。<s:textfield value="%{#request.msg}"/>
-
{ } 中值用
''
引起來乏悄,此時不再是 ognl 表達式,而是普通的字符串恳不。例如:
<s:property value="%{'#request.msg'}"/>
-
-
$
符號的用法-
在配置文件(struts.xml)中可以使用 OGNL 表達式檩小,例如:文件下載的配置文件。
<action name="download1" class="cn.itcast.demo2.DownloadAction"> <result name="success" type="stream"> <param name="contentType">${contentType}</param> <param name="contentDisposition">attachment;filename=${downFilename}</param> </result> </action>
-
PS:學(xué)習(xí)值棧的時候感覺有點蒙烟勋,內(nèi)部結(jié)構(gòu)有點復(fù)雜规求,不太理解為什么在 Struts2 里面會有值棧這個東西存在,先學(xué)著怎么用吧 :)