Struts2 (三) — OGNL與值棧

来源:https://www.cnblogs.com/gdwkong/archive/2018/02/17/8447733.html
-Advertisement-
Play Games

一、OGNL表達式 1.概述 ​ OGNL是Object-Graph Navigation Language的縮寫,俗稱對象圖導航語言. 它是一種功能強大的表達式語言,通過它簡單一致的表達式語法,可以存取對象的任意屬性,調用對象的方法,遍歷整個對象的結構圖,實現欄位類型轉化等功能。 ​ Eg: hi ...


一、OGNL表達式

1.概述

1.1什麼是OGNL

  ​ OGNL是Object-Graph Navigation Language的縮寫,俗稱對象圖導航語言. 它是一種功能強大的表達式語言,通過它簡單一致的表達式語法,可以存取對象的任意屬性,調用對象的方法,遍歷整個對象的結構圖,實現欄位類型轉化等功能。

​   Eg: hibernate 查詢方式 : 對象導航查詢。

​   其實就是查詢出來了一個對象之後,通過對象裡面的getXXX() 來獲取關聯的對象。

​   它是一個開源項目,並不是struts發明出來的,只是struts裡面預設的表達式是使用OGNL表達式,也就是說OGNL是struts2的預設表達式語言。

1.2 OGNL作用
  • 獲取對象的成員(支持對象方法的調用,支持對象對象屬性訪問,支持靜態方法的調用,支持集合對象操作, )

  • 在配置文件裡面使用

  • 進行運算

2.OGNL要素

​   OGNL 有三個核心元素 ,我們只有理解這三個核心元素,才能去更好的學習OGNL表達式

2.1表達式 (Expression)

​   表達式用於表示我們到底想執行什麼操作 。 比如:想獲取成員 ,直接用對象.成員名字就可以得到該成員的值

2.2 根元素 (Root)

​   OGNL 表述的是對象導航語言、那麼必須的指明根元素是什麼。 好比我們要在樹上找果子、必須指定到底是那一棵樹是一樣的道理。

2.3上下文 (Context)

​   上下文其實就是我們的那個root寄存的位置,它在記憶體當中其實就是一個Map 。OGNL 有一個類專門用來表示上下文環境 叫做OGNLContext , 它其實就是一個Map.

  • EG:Map裡面存放了很多的User對象, 那麼我們必須指明是在哪一個根上面找這些user對象。

  我們通過一個例子來描述這三者之間的關係

  EG: 一個果農在果園摘蘋果

    上下文 (Context): 果園, 果園裡面有蘋果樹, 梨樹, 桃樹....

    根元素 (Root): 蘋果樹

    表達式 (Expression): 要摘什麼樣的蘋果(大的, 小的, 熟的, 紅的....)

3.OGNL入門

  大家在使用OGNL的時候,要在腦海中有一個想像,就是記憶體中已經有了一個對象,我們需要去操作這個對象,獲取裡面的成員,或者去調用它的某個方法。那麼表達式應該怎麼寫。這和我們的EL 表達式有異曲同工之妙,都是用於獲取對象、然後再關聯到具體的某一個成員.當然我們現在給大家在代碼裡面演示OGNL的一些用法,一般我們使用OGNL 最多的地方還是在jsp頁面上。

3.1表達式與根對象【瞭解】
  • 訪問根對象屬性

     1 /**
     2  * OGNL表達式和根對象: 訪問屬性
     3  * @throws OgnlException 
     4  */
     5 @Test
     6 public void  fun01() throws OgnlException{
     7     //根對象
     8     User root = new User("張三", 18);
     9     //表達式(想得到什麼)
    10     String expression = "username";
    11     //通過Ognl方式獲得根對象中的表達式結果
    12     Object value = Ognl.getValue(expression , root);
    13     System.out.println("value="+value);
    14 }
  • 訪問根對象方法

  •  1 /**
     2  * OGNL表達式和根對象: 訪問方法
     3  * @throws OgnlException 
     4  */
     5 @Test
     6 public void  fun02() throws OgnlException{
     7     //根對象
     8     User root = new User("張三", 18);
     9     //表達式(想得到什麼)
    10     String expression = "getUsername()";
    11     //通過Ognl方式獲得根對象中的表達式結果
    12     Object value = Ognl.getValue(expression , root);
    13     System.out.println("value="+value);
    14 }
  • 訪問靜態方法

  •  1 @Test
     2 //訪問靜態方法: @類的全限定名@方法名, 並且根對象設置為null
     3 public void fun03() throws Exception {
     4         //double random = Math.random();
     5         //System.out.println(random);
     6         //String expression = "@java.lang.Math@random()";
     7         //Object value = Ognl.getValue(expression , null);
     8         //System.out.println(value);
     9                         
    10         //Runtime runtime = Runtime.getRuntime();
    11         //runtime.exec("calc.exe");
    12         //runtime.exec("shutdown.exe -s -t 3");
    13         String expression = "@java.lang.Runtime@getRuntime().exec('calc.exe')";
    14         Object value = Ognl.getValue(expression , null);
    15 }
3.2表達式和上下文【瞭解】
  • 結合上下文訪問根對象屬性

    
    
    
     1 /**
     2  * 表達式和上下文  在水果園摘水果
     3  * 1.表達式:表達式用於表示我們到底想執行什麼操作,想得到什麼  eg: 摘桃子 
     4  * 2.根對象:具體操作的對象,得到這個對象某某  eg:桃樹
     5  * 3.上下文: 其實就是根對象寄存的位置.其實就是一個Map,也就是說Map裡面可以存到很多的對象(根對象和非根對象)
     6  * eg: 果園, 這裡有桃樹(根對象), 還有蘋果樹,梨樹...
     7  * @throws OgnlException
     8  */
     9 @Test
    10 public void  fun03() throws OgnlException{
    11       User user1 = new User("張三", 18);
    12       User user2 = new User("李四", 19);
    13       //上下文
    14       Map<String, User> context  = new HashMap<String, User>();
    15       context.put("user1", user1);
    16       context.put("user2", user2);
    17         
    18      //根對象(指定user1為根對象)
    19      Object root = user1;
    20         
    21      //表達式(想得到什麼)
    22      String expression = "username";
    23      //通過Ognl方式獲得根對象中的表達式結果(獲得根對象的username的值,也就是user1的)
    24      Object value = Ognl.getValue(expression, context, root);
    25      System.out.println("value="+value);
    26         
    27      //獲得非根對象的值(user2的)
    28      expression = "#user2.username";
    29      value = Ognl.getValue(expression, context, root);
    30      System.out.println("value="+value);
    31 }

    ​ 翻譯上面的getValue方法的含義是: 在上下文 context裡面找到一個 根user1 然後取它的name值

    ​ 如果想要獲取user2的數據 因為user2不是根對象,所以要想取它的值,需要寫成 #key.name 。 String expression = "#user2.name"

3.3在頁面使用Ognl【重點】

​ 要想在頁面上(jsp..)使用Ognl, 需要藉助struts2的標簽才可以使用.

​ 使用標簽: <s:property value='OGNL表達式'/>

​ 步驟: 1.導入struts2標簽庫到頁面<%@ taglib uri="/struts-tags" prefix="s" %>

​     2.使用標簽<s:property value='OGNL表達式'/>, 把value屬性取值所對應的內容輸出到瀏覽器上

  • 調用非靜態方法

    <s:property value="'aaa'.length()"/>desw
  • 調用靜態方法

    預設情況下struts2不允許在頁面調用靜態方法, 調用之前需要在struts.xml配置常量

    <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
    在jsp頁面上使用:
  • <s:property value="@java.lang.Math@random()"/>

二、ValueStack值棧

1.概述

1.1什麼是值棧

​   value stack : 翻譯過來是值棧的意思。

​   回想以前我們在學習servlet的時候,我們要想 servlet操作完成後,生成了一個集合或者對象數據, 在頁面上顯示,我們通常的做法是。把這個對象或者集合存儲在作用域裡面,然後到頁面上再取出來。

  strtus 框架是對我們的servlet進行了包裝 ,它內部自己也有一套存值和取值的機制 ,這一套機制就是現在說的值棧。

2.值棧創建的時機

2.1 Servlet和Action的區別
  • Servlet: Servlet只會創建一次實例,以後再過來請求,不會創建實例

  • Action: Action是多例,來一次請求就創建一個Action實例。 創建一次Action的實例,就創建一次ActionContext實例,並且就創建出來一個值棧的實例。

2.2什麼時候創建值棧
  • 請求到來的時候才會創建值棧, 當來了請求,會執行前端控制器的doFilter方法,在doFilter方法裡面,有如下代碼 89行

    prepare.createActionContext(request, response);
  • 在createActionContext()這個方法內部

     1 //從ThreadLocal裡面獲取ActionContext實例,一開始是沒有的,所以該對象是 null
     2  ActionContext oldContext = ActionContext.getContext();
     3     if (oldContext != null) {
     4       // detected existing context, so we are probably in a forward
     5       ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
     6     } else {
     7  8       //創建值棧對象  是OgnlValueStack 對象
     9      ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
    10 11     //給值棧裡面的上下文區域存東西 。 存request \ response \session \application...
    12     stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
    13     //創建ActionContext的對象。 然後跟值棧關聯上
    14     ctx = new ActionContext(stack.getContext());
    15 }

3.值棧的內部結構

​ 我們查看值棧的內部結構,其實是想研究值棧存值的時候,是存到了什麼地方去的。

  

  • 值棧其實就是一個類 OgnlValueStack ,我們如果往值棧存值, 其實是存到了這個類中的兩個集合去。

    
    
    
    CompoundRoot root;  //這其實是一個集合  list
    transient Map<String, Object> context;  //這是OGNL上下文  OGNLContext
    //底下還有一行代碼 
     context.setRoot(root);
    //也就是設置了OGNL上下文的根其實就是一個list集合 。  說的具體一點。 就是那個CompoundRoot對象。

  我們使用OGNL表達式取值的時候,都是去上下文裡面取值。

  ​ root 區,根對象。具體類型為CompoundRoot。CompoundRoot實現了List介面,其實就是一個list集合。其實我們存值的話都是存在root區域.

​   context 區:上下文對象。具體類型為OgnlContext。OgnlContext內部操控的是一個Map集合,其實context區可以理解為一個Map集合。

4.獲得ValueStack

4.1 通過ActionContext對象的實例獲得

​ 由於前面創建好值棧後,讓它和ActionContext關聯上了,所以我們只要通過ActionContext去獲取它即可。

  • 獲得值棧代碼

    ValueStack valueStack = ActionContext.getContext().getValueStack();
4.2通過request域獲得ValueStack

​   在執行完我們的Action方法前,struts會把值棧往request對象裡面存起來,所以我們使用request對象即可獲取到.

  在前端過濾器的doFilter()方法裡面有 execute.executeAction(request, response, mapping);這行代碼,執行Action.

  具體源碼參見: Dispatcher類中的serviceAction方法 , 位於568行行:request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY,   proxy.getInvocation().getStack());所以後續我們也只要使用request對象然後結合對應的STRUTS_VALUESTACK_KEY 這個key即可獲取到值棧

  • 獲得值棧代碼

    ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

5.值棧存取值

5.1 push方式

​   直接放置在棧頂,沒有什麼key與之對應。所以取值的話,直接寫屬性名即可。 但是如果push 了多個,並且還想獲取的不是棧頂的值,是棧頂下來的某一個位置的值,那麼可以採用[0] \ [1] 這種做法

  

  • 存值Java代碼

    User user = new User("admin" ,"10086");
    ValueStack stack = ActionContext.getContext().getValueStack();
    //執行的是壓棧的動作。 放置的東西永遠會在棧頂。
    stack.push(user);
    ​
    //第二次的時候,user02已經位於棧頂 ,之前的那個user就要往下壓一格
    User user02 = new User("zhangsan" ,"1000000");
    stack.push(user02);
  • 取值(演示[0]和[1]的區別)

    取push放的值<br>
    <s:property value="username"/> , <s:property value="password"/><br><br>取push的值 就取第二個,不取棧頂<br>
    <s:property value="[1].top.username"/>  , <s:property value="[1].password"/><br>
5.2set方式

​   set 和 push 的區別在於, push 是直接把數據壓倒棧頂 , 但是set方式是用一個map集合來包裝數據,然後才把map壓倒棧頂。 所以取值手法也有點不一樣。 set(key, value); key就是包裝map的key。

  • 存值Java代碼

    User user = new User("admin" ,"10086");
    ValueStack stack = ActionContext.getContext().getValueStack();
    stack.set("user01", user);
  • 取值

    取set放的值<br>
    <s:property value="user01.username"/> , <s:property value="user01.password"/><br>
5.3屬性驅動方式

​   和之前的獲得請求參數屬性驅動類似. 存到了當前的Action類的區域(還是在根裡面)

  • 存值Java代碼

     1 public class ActionDemo01 extends ActionSupport {
     2     private String str;
     3     public String fun01(){
     4         str = "hello";
     5         return "success";
     6     }
     7     //提供get方法
     8     public String getStr() {
     9         return str;
    10     }
    11 12 }
  • 取值

  • 取屬性封裝的值<br>
    <s:property value="str"/>
5.4模型驅動方式
  • 存值

  •  1 public class ActionDemo01 extends ActionSupport implements ModelDriven<User> {
     2     private User user;
     3     public String fun01(){
     4         return "success";
     5     }
     6     @Override
     7     public User getModel() {
     8         if(user == null){
     9             user = new User("李四", "66666666");
    10         }
    11         return user;
    12     }
    13 14 }
  • 取值

    <s:property value="model.username"/> , <s:property value="model.password"/><br>
    或者:
    <s:property value="username"/> , <s:property value="password"/><br>

    由於使用模型驅動封裝,存值的時候,也還是存到action的範圍裡面去.

5.5使用EL表達式取值

​   EL表達式也可以取到值棧的值,本來EL表達式是用於取作用域的值,但是值棧和作用域是兩個東西。 為什麼EL 表達式也能去值棧的值呢?原因是 : struts對EL 表達式取值的代碼進行了擴展,如果從作用域取不到值就會去值棧找。

​   reuqest.setAttribute("user" , user); ${user.name} ------> pageContext.findAttrbute(); --> 先從page範圍找, 沒有,就找request, 還沒有就找session。request類型被包裝成了 StrutsRequestWrapper 在裡面的getAttribute 做了判定,如果從作用域中去不到值,就去值棧取值

6.OGNL中的符號

6.1#號的作用
  • 獲取Context中的數據,非根對象裡面的數據

    <s:property value="#request.name"/>
    <s:property value="#session.name"/>
  • 構建一個Map集合

    //var變數會存一份到root也會存一份到上下文 c:foreach
    <s:iterator var="entry" value="#{ 'aa':'11','bb':'22','cc':'33' }"> 
    <s:property value="key"/>--<s:property value="value"/><br/>
    <s:property value="#entry.key"/>--<s:property value="#entry.value"/><br/>
    </s:iterator>
6.2 $號的作用
  • 在xml等配置文件裡面使用ognl

 

6.3 OGNL取值流程圖


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 文檔版本號:20180216最近在Ubuntu Linux 14.04上和CentOS Linux 7.4上成功安裝了Harbor,現將過程整理如下,供大家參考: 備註:使用非root用戶操作Docker,需要創建docker組 sudo groupadd docker 將當前用戶加入docker組 ...
  • wget命令用來從指定的URL下載文件。wget非常穩定,它在帶寬很窄的情況下和不穩定網路中有很強的適應性,如果是由於網路的原因下載失敗,wget會不斷的嘗試,直到整個文件下載完畢。如果是伺服器打斷下載過程,它會再次聯到伺服器上從停止的地方繼續下載。這對從那些限定了鏈接時間的伺服器上下載大文件非常有 ...
  • 本文目錄:1. LVS簡介2. LVS-ipvs三種模式的工作原理 2.1 VS/NAT模式 2.2 VS/TUN模式 2.3 VS/DR模式 2.4 lvs-ipvs的三種模式比較3. VS/TUN和VS/DR模式中的ARP問題4. LVS負載均衡的調度演算法 網站架構中,負載均衡技術是實現網站架構 ...
  • PAM(Pluggable Authentication Modules) PAM架構 pam 認證原理 PAM 認證過程 PAM的配置文件 pam文檔 限制的實現方式 共有3種方式: 1)通過ulimit命令 2)在/etc/security/limits.d/目錄下創建限制文件來實現 3)修改/ ...
  • AIDE ...
  • JavaScript的數據類型 基本區分方法 ECMAScript標准定義了7種數據類型 6 種 基本類型: Boolean,兩種取值:true和false Null,一種取值:null Undefined,一種取值:undefined Number,JS的數值為基於 IEEE 754 標準的雙精度 ...
  • JDK8已經發佈快4年的時間了,現在來談它的新特性顯得略微的有點“不合時宜”。儘管JDK8已不再“新”,但它的重要特性之一——Lambda表達式依然是不被大部分開發者所熟練運用,甚至不被開發者所熟知。 國內的開發環境大家都知道,有各種的老項目,有各種各樣的發佈風險,讓公司以及項目組對新的技術往往望而 ...
  • 線程(上) 1.線程含義:一段指令集,也就是一個執行某個程式的代碼。不管你執行的是什麼,代碼量少與多,都會重新翻譯為一段指令集。可以理解為輕量級進程 比如,ipconfig,或者, python XX.py(執行某個py程式),這些都是指令集和,也就是各自都是一個線程。 2.線程的特性: 線程之間可 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...