Struts2 之 OGNL 與值棧

本文包括:

1摩幔、OGNL 表達式概述(了解)

2、值棧概述

3铐尚、值棧的存值與取值

4拨脉、EL 表達式也會獲取到值棧中的數(shù)據(jù)

5、總結(jié) OGNL 表達式的特殊的符號

1宣增、OGNL 表達式概述(了解)

  1. OGNL 是 Object Graphic Navigation Language(對象圖導(dǎo)航語言)的縮寫

    • 所謂對象圖玫膀,即以任意一個對象為根,通過 OGNL 可以訪問與這個對象關(guān)聯(lián)的其它對象

    • 通過它簡單一致的表達式語法爹脾,可以存取對象的任意屬性帖旨,調(diào)用對象的方法,遍歷整個對象的結(jié)構(gòu)圖灵妨,實現(xiàn)字段類型轉(zhuǎn)化等功能解阅。它使用相同的表達式去存取對象的屬性

  2. Struts2 框架使用 OGNL 作為默認的表達式語言

    • OGNL 是一種比 EL 強大很多倍的語言

    • xwork 提供 OGNL表達式

    • ognl-3.0.5.jar

  3. OGNL 提供五大類功能

    • 支持對象方法調(diào)用

    • 支持類靜態(tài)的方法調(diào)用和值訪問

    • 訪問 OGNL 上下文(OGNL context)和 ActionContext

    • 支持賦值操作和表達式串聯(lián)

    • 操作集合對象

  4. 測試的代碼

     // 訪問對象的方法
     @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 表達式

  1. Struts2 引入了 OGNL 表達式,主要是在 JSP 頁面中獲取值棧中的值

  2. 具體在 Struts2 中怎么使用呢泌霍?如下步驟:

    • 需要先引入 Struts2 的標(biāo)簽庫

        <%@ taglib prefix="s" uri="/struts-tags" %>
      
    • 使用 Struts2 提供的標(biāo)簽中的標(biāo)簽货抄,如下的 property 標(biāo)簽會從值棧中取值

        <s:property value="OGNL表達式"/>
      
  3. 在 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é)幾個小問題:

        1. 訪問 root 中數(shù)據(jù) 不需要#

        2. 訪問 context 其它對象數(shù)據(jù) 加 #

        3. 如果向 root 中存入對象的話者填,優(yōu)先使用 push 方法。

        4. 如果向 root 中存入集合的話,優(yōu)先要使用 set 方法白嘁。

      • 在 Context 中獲取數(shù)據(jù)(context 棧)

        1. 在Action中向域?qū)ο笾写嫒胫?/p>

        2. request:

           <s:property value="#request.username"/>
          
        3. session:

           <s:property value="#session.username"/>
          
        4. application:

           <s:property value="#application.username"/>
          
        5. attr:

           <s:property value="#attr.username"/>
          
        6. 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 表達式的特殊的符號

  1. # 符號的用法

    • 獲得 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>
        
  2. % 符號的用法

    • 強制字符串解析成 OGNL 表達式朗儒。

      例如:在 request 域中存入值颊乘,然后在文本框(<s:textfield>)中取值,現(xiàn)在到value上醉锄。

        <s:textfield value="%{#request.msg}"/>
      
    • { } 中值用''引起來乏悄,此時不再是 ognl 表達式,而是普通的字符串恳不。

      例如:

        <s:property value="%{'#request.msg'}"/>
      
  3. $ 符號的用法

    • 在配置文件(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é)著怎么用吧 :)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卵惦,一起剝皮案震驚了整個濱河市阻肿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沮尿,老刑警劉巖丛塌,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡姨伤,警方通過查閱死者的電腦和手機哨坪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乍楚,“玉大人当编,你說我怎么就攤上這事⊥较” “怎么了忿偷?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長臊泌。 經(jīng)常有香客問我鲤桥,道長,這世上最難降的妖魔是什么渠概? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任茶凳,我火速辦了婚禮,結(jié)果婚禮上播揪,老公的妹妹穿的比我還像新娘贮喧。我一直安慰自己,他們只是感情好猪狈,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布箱沦。 她就那樣靜靜地躺著,像睡著了一般雇庙。 火紅的嫁衣襯著肌膚如雪谓形。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天疆前,我揣著相機與錄音寒跳,去河邊找鬼。 笑死峡继,一個胖子當(dāng)著我的面吹牛冯袍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播碾牌,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼儡循!你這毒婦竟也來了舶吗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤择膝,失蹤者是張志新(化名)和其女友劉穎誓琼,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡腹侣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年叔收,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片傲隶。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡饺律,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跺株,到底是詐尸還是另有隱情复濒,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布乒省,位于F島的核電站巧颈,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏袖扛。R本人自食惡果不足惜砸泛,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蛆封。 院中可真熱鬧晾嘶,春花似錦、人聲如沸娶吞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妒蛇。三九已至机断,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绣夺,已是汗流浹背吏奸。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留陶耍,地道東北人奋蔚。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像烈钞,于是被迫代替她去往敵國和親泊碑。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 概述 什么是Struts2的框架Struts2是Struts1的下一代產(chǎn)品毯欣,是在 struts1和WebWork的...
    inke閱讀 2,255評論 0 50
  • ValueStack是一個接口馒过,在struts2中使用OGNL(Object-Graph Navigation L...
    重山楊閱讀 1,685評論 0 7
  • action中如何接受頁面?zhèn)鬟^來的參數(shù) 第一種情況:(同名參數(shù)) 例如:通過頁面要把id=1 name=tom a...
    清楓_小天閱讀 2,958評論 1 22
  • 標(biāo)簽 如果要配置的標(biāo)簽,那么必須要先配置標(biāo)簽酗钞,代表的包的概念腹忽。 包含的屬性 name包的名稱来累,要求是唯一的,管理a...
    偷偷得路過閱讀 1,343評論 0 0
  • 非本人總結(jié)的筆記窘奏,抄點筆記復(fù)習(xí)復(fù)習(xí)嘹锁。感謝傳智博客和黑馬程序猿記筆記啊記筆記 Ognl的簡介 Ognl是獨立的項目,...
    鍵盤瞎閱讀 520評論 0 2