Struts2之 OGNL表達式和值棧



    
技術分析之OGNL表達式概述(了解)
    
    1. OGNL是Object Graphic Navigation Language(對象圖導航語言)的縮寫
        * 所謂對象圖,即以任意一個對象為根,通過OGNL可以訪問與這個對象關聯的其它對象
        * 通過它簡單一致的表達式語法,可以存取對象的任意屬性,調用對象的方法,遍歷整個對象的結構圖,實現字段類型轉化等功能。它使用相同的表達式去存取對象的屬性
    
    2. Struts2框架使用OGNL作為默認的表達式語言
        * OGNL是一種比EL強大很多倍的語言
        * xwork提供 OGNL表達式
        * ognl-3.0.5.jar
    
    3. OGNL 提供五大類功能
       * 支持對象方法調用
       * 支持類靜態的方法調用和值訪問
       * 訪問OGNL上下文(OGNL context)和ActionContext
       * 支持賦值操作和表達式串聯
       * 操作集合對象
    
    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);
        }


    
技術分析之在Struts2框架中使用OGNL表達式
    
    1. Struts2引入了OGNL表達式,主要是在JSP頁面中獲取值棧中的值
    2. 具體在Struts2中怎么使用呢?如下步驟
        * 需要先引入Struts2的標簽庫        

<%@ taglib prefix="s" uri="/struts-tags" %>  

        * 使用Struts2提供的標簽中的標簽   

<s:property value="OGNL表達式"/>    

    3. 在JSP頁面使用OGNL表達式
        * 訪問對象方法

   <s:property value="'hello'.length()"/>   

   
技術分析之值棧的概述
    
    1. 問題一:什么是值棧?
        * 值棧就相當於Struts2框架的數據的中轉站,向值棧存入一些數據。從值棧中獲取到數據。
        * ValueStack 是 struts2 提供一個接口,實現類 OgnlValueStack ---- 值棧對象 (OGNL是從值棧中獲取數據的 )
        * Action是多例的,有一起請求,創建Action實例,創建一個ActionContext對象,代表的是Action的上下文對象,還會創建一個ValueStack對象。
        * 每個Action實例都有一個ValueStack對象 (一個請求 對應 一個ValueStack對象 )
        * 在其中保存當前Action 對象和其他相關對象
        * Struts 框架把 ValueStack 對象保存在名為 “struts.valueStack” 的請求屬性中,request中 (值棧對象 是 request一個屬性)
            * ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack");
    

    
技術分析之值棧的內部結構
    
    2. 問題二 : 值棧的內部結構 ?
        * 值棧由兩部分組成
            > root        -- Struts把動作和相關對象壓入 ObjectStack 中--List
            > context      -- Struts把各種各樣的映射關系(一些 Map 類型的對象) 壓入 ContextMap 中
        
        * Struts會默認把下面這些映射壓入ContextMap(context)中
            * 注意:request代表的是Map集合的key值,value的值其實也是一個Map集合。
            
            > parameters: 該 Map 中包含當前請求的請求參數  ?name=xxx&password=123
            > request: 該 Map 中包含當前 request 對象中的所有屬性
            > session: 該 Map 中包含當前 session 對象中的所有屬性
            > application:該 Map 中包含當前 application  對象中的所有屬性
            > attr: 該 Map 按如下順序來檢索某個屬性: request, session, application
        
        * ValueStack中 存在root屬性 (CompoundRoot) 、 context 屬性 (OgnlContext )
            > CompoundRoot 就是ArrayList
            > OgnlContext 就是 Map
        
        * context 對應Map 引入 root對象
            > context中還存在 request、 session、application、 attr、 parameters 對象引用
            > OGNL表達式訪問值棧中的數據
                * 訪問root中數據時 不需要 #
                * 訪問 request、 session、application、 attr、 parameters 對象數據 必須寫 #
            
            > 操作值棧 默認指 操作 root 元素

    
技術分析之值棧的創建和ActionContext對象的關系
    
    3. 問題三 : 值棧對象的創建,ValueStack 和 ActionContext 是什么關系?
        * 值棧對象是請求時創建的
        * ActionContext是綁定到當前的線程上,那么在每個攔截器或者Action中獲取到的ActionContext是同一個。
        * ActionContext中存在一個Map集合,該Map集合和ValueStack的context是同一個地址。
        * ActionContext中可以獲取到ValueStack的引用,以后再開發,使用ActionContext來獲取到值棧對象
    

    
技術分析之獲取到值棧的對象
    
    4. 問題四 : 如何獲得值棧對象
        * 獲得值棧對象 有三種方法

     ValueStack vs1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
     ValueStack vs2 = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
     ValueStack vs3 = ActionContext.getContext().getValueStack();


    
技術分析之向值棧中保存數據
    
    5. 問題五: 向值棧保存數據 (主要針對root棧)
        > valueStack.push(Object obj);
            * push方法的底層調用root對象的push方法(把元素添加到0位置)
        
        > valueStack.set(String key, Object obj);
            * 源碼獲取map集合(map有可能是已經存在的,有可能是新創建的),把map集合push到棧頂,再把數據存入到map集合中。
        
        > 在jsp中 通過 <s:debug /> 查看值棧的內容


    
技術分析之從值棧中獲取值
    
    6. 問題六: 在JSP中獲取值棧的數據
        * 總結幾個小問題:
            > 訪問root中數據 不需要#
            > 訪問context其它對象數據 加 #
            > 如果向root中存入對象的話,優先使用push方法。
            > 如果向root中存入集合的話,優先要使用set方法。
        
        * 在OgnlContext中獲取數據
            > 在Action中向域對象中存入值
            > 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"/>
    
    6.1 代碼如下
    

   <!--
            // vs.push("美美");
            // 獲取到棧頂的值
            <s:property value="[0].top"/>
        -->
        
        <!--
            // 棧頂是map集合,通過key獲取值
            vs.set("msg", "小鳳");
            <s:property value="[0].top.msg"/>
        -->
        
        <!--  
            vs.push(user);
            // 棧頂放user對象
            <s:property value="[0].top.username"/>
            <s:property value="[0].top.password"/>
            // [0].top 關鍵字是可以省略的  findValue()
            <s:property value="username"/>
        -->
        
        <!--
            vs.set("user", user);
            <s:property value="[0].top.user.username"/>
            <s:property value="[0].top.user.password"/>
            // 省略關鍵字
            <s:property value="user.username"/>
        -->
        
        <!--  
            // 在ValueStack1Action提供了成員的屬性
            private User user = new User("小澤","456");
            public User getUser() {
                return user;
            }
            public void setUser(User user) {
                this.user = user;
            }
            
            User user = new User("小蒼","123");
            vs.set("user", user);
            // 從棧頂開始查找,找user的屬性,顯示名稱    返回的小蒼
            <s:property value="user.username"/>
            
            // [1].top獲取ValueStack1Action [1].top.user返回user對象  [1].top.user.username獲取對象的屬性名稱
            <s:property value="[1].top.user.username"/>
        -->
        
        <!--  
            棧頂是list集合
            vs.push(ulist);
            <s:property value="[0].top[0].username"/>
            <s:property value="[0].top[1].username"/>
        -->
        
        <!--
            vs.set("ulist", ulist);
            <s:property value="ulist[0].username"/>
        -->
        
        <!-- 迭代的標簽
            屬性
                * value    要迭代的集合,需要從值棧中獲取
                * var    迭代過程中,遍歷的對象
                    * var編寫上,把迭代產生的對象默認壓入到context棧中,從context棧取值,加#號
                    * var不編寫,默認把迭代產生的對象壓入到root棧中
            
            for(User user:ulist){}    
            // 編寫var的屬性
            <s:iterator value="ulist" var="u">
                <s:property value="#u.username"/>
                <s:property value="#u.password"/>
            </s:iterator>
            
            // 沒有編寫var關鍵字
            <s:iterator value="ulist">
                <s:property value="username"/>
                <s:property value="password"/>
            </s:iterator>
        -->
        
        <!-- 從context棧中獲取值,加#號
        
        HttpServletRequest request = ServletActionContext.getRequest();
        request.setAttribute("msg", "美美");
        request.getSession().setAttribute("msg", "小風");
        
        <s:property value="#request.msg"/>
        <s:property value="#session.msg"/>
        <s:property value="#parameters.id"/>
        <s:property value="#attr.msg"/>
        -->
        
        <!-- 在JSP頁面上,查看值棧的內部結構 -->
        <s:debug></s:debug>
    

 

    
技術分析之EL表達式也會獲取到值棧中的數據
    
    7. 問題七:為什么EL也能訪問值棧中的數據?
        * StrutsPreparedAndExecuteFilter的doFilter代碼中 request = prepare.wrapRequest(request);     
            > 對Request對象進行了包裝 ,StrutsRequestWrapper
            > 增強了request的 getAttribute         

  Object attribute = super.getAttribute(s);
                if (attribute == null) {
                   attribute = stack.findValue(s);
                }

             > 訪問request范圍的數據時,如果數據找不到,去值棧中找
            > request對象 具備訪問值棧數據的能力 (查找root的數據)
    

    
總結OGNL表達式的特殊的符號
    
    1. # 符號的用法
        * 獲得contextMap中的數據

  <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"/>
        

        * 構建一個map集合
            * 例如:

     <s:radio name="sex" list="{'男','女'}"></s:radio><s:radio name="sex" list="#{'0':'男','1':'女'}"></s:radio>


    
    2. % 符號的用法
        * 強制字符串解析成OGNL表達式。
            > 例如:在request域中存入值,然后在文本框(<s:textfield>)中取值,現在到value上

<s:textfield value="%{#request.msg}"/>        

        * { }中值用''引起來,此時不再是ognl表達式,而是普通的字符串

<s:property value="%{'#request.msg'}"/>

 
    3. $ 符號的用法
        * 在配置文件中可以使用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>

 


       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM