java struts2值栈ognl_Struts2 (三) — OGNL与值栈
一、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 *@throwsOgnlException4 */
5 @Test6 public void fun01() throwsOgnlException{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 *@throwsOgnlException4 */
5 @Test6 public void fun02() throwsOgnlException{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 @Test2 //訪問靜態方法: @類的全限定名@方法名, 并且根對象設置為null
3 public void fun03() throwsException {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 *@throwsOgnlException8 */
9 @Test10 public void fun03() throwsOgnlException{11 User user1 = new User("張三", 18);12 User user2 = new User("李四", 19);13 //上下文
14 Map context = new HashMap();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的標簽才可以使用.
? 使用標簽:
? 步驟: 1.導入struts2標簽庫到頁面
? ?2.使用標簽, 把value屬性取值所對應的內容輸出到瀏覽器上
調用非靜態方法
desw
調用靜態方法
默認情況下struts2不允許在頁面調用靜態方法, 調用之前需要在struts.xml配置常量
在jsp頁面上使用:
二、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(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 = newActionContext(stack.getContext());15 }
3.值棧的內部結構
? 我們查看值棧的內部結構,其實是想研究值棧存值的時候,是存到了什么地方去的。
值棧其實就是一個類 OgnlValueStack ,我們如果往值棧存值, 其實是存到了這個類中的兩個集合去。
CompoundRoot root; //這其實是一個集合 list
transient Map 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放的值
,
?
取push的值 就取第二個,不取棧頂
,
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放的值
,
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 returnstr;10 }11 ?12 }
取值
取屬性封裝的值
5.4模型驅動方式
存值
1 public class ActionDemo01 extends ActionSupport implements ModelDriven{2 privateUser user;3 publicString fun01(){4 return "success";5 }6 @Override7 publicUser getModel() {8 if(user == null){9 user = new User("李四", "66666666");10 }11 returnuser;12 }13 ?14 }
取值
,
或者: ,
由于使用模型驅動封裝,存值的時候,也還是存到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中的數據,非根對象里面的數據
構建一個Map集合
//var變量會存一份到root也會存一份到上下文 c:foreach
--
--
6.2 $號的作用
在xml等配置文件里面使用ognl
6.3 OGNL取值流程圖
總結
以上是生活随笔為你收集整理的java struts2值栈ognl_Struts2 (三) — OGNL与值栈的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 数据类型转换的一场_Java基
- 下一篇: 邹妓是谁画的呢?