複習_Struts2_OGNL表達式、值棧

1.OGNL的用法詳解

Ognl表達式一般是結合struts2框架的標籤來使用的,最重要的作用,就是獲取值棧中存儲的數據。

在下面的示例,咱們將對ognl的各種用法進行測試,分析OGNL是如何使用的。

  1. 1.Java對象的直接訪問
    例如:’itcast’
  2. 實例方法調用
    語法:object.doSomething(),object是對象實例
    例如:’itcast’.toUpperCase()
  3. 靜態方法調用(類的靜態方法或靜態變量的訪問)–不常用
    語法:@[類全名(包括包路徑)]@[方法名|值名]
    例如:@java.lang.Math@max(10,20)
    注意:必須通過配置struts2的常量來開啓靜態方法調用功能:
    struts.ognl.allowStaticMethodAccess=true
  4. 賦值操作或表達式串聯。–可以運算
    如計算1+2或者2*2
  5. 對集合對象的操作
    <s:property value="#{‘上海’:‘浦東’,‘北京’:‘豐臺’}"/>
  6. Ognl對值棧的操作

示例:
搭建struts2的環境:導入jar、struts.xml、web.xml,引入struts標籤庫,編寫表達式代碼

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
    <title>$Title$</title>
</head>
<body>
<!-- s:property標籤中的value可以用來解析ognl表達式獲取各類數據 -->

<!-- ognl表達式的用法 -->

<!-- s:property標籤中的value 可以解析ognl表達式 -->
<s:property value="'itcast'"/><br>
<!-- 可以操作對象的實例方法 -->
<s:property value="'itcast'.toUpperCase()"/><br>
<!-- 可以操作靜態方法 -->
<s:property value="@java.lang.Math@max(1,20)"/><br>
<!-- 操作集合 -->
<s:property value="{'上海','北京'}"/><br>
<!-- 操作map集合 -->
<s:property value="#{'上海':'浦東','北京':'豐臺' }"/><br>
<!-- 操作表達式 -->
<s:property value="10%3"/><br>
</body>
</html>

應用ognl 表達式調用對象方法或者靜態方法,需要結合struts2的\<s:property > 標籤,其中提供的value屬性支持ognl。

2.值棧概述

2.1 值棧(ValueStack)是什麼

值棧(ValueStack),是Struts2的數據中轉站(類似於超市)

他自動保存了當前Action對象和其他相關對象(包括常用的Web對象的引用,如request、application、session等),也可以手動保存自己的數據對象,同時也可以隨時隨地將對象從值棧取出或操作(通過OGNL表達式)

最核心的作用:存值

值棧(ValueStack)的接口是ValueStack類,實現類是OgnlValueStack類,該對象是Struts2利用OGNL的基礎。只要我們用到ognl,那麼就需要依賴值棧。

在這裏插入圖片描述
Struts2框架將ValueStack對象保存在名爲“struts.valueStack”的請求(request)屬性中,即值棧是request中的一個對象,一個請求對應一個Action實例和一個值棧對象。

值棧的獲取方式:

  • request.getAttribute(“struts.valueStack”)
  • ActionContext.getContext().getValueStack()

該兩種方式獲取的值棧其實是同一個。


2.2 值棧的數據存儲結構的分析

【示例1】
目標:值棧對象的獲取,並證明兩種方式獲取的對象是同一個對象:
1)request.getAttribute(“struts.valueStack”)
2)ActionContext.getContext().getValueStack()

步驟一:編寫action
創建包和action
public class DemoAction extends ActionSupport{
	@Override
	public String demo(){
		//獲取值棧的第一種方式
		ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
		//獲取值棧的第二種方式
		ValueStack valueStack2 = ActionContext.getContext().getValueStack();
		System.out.println(valueStack1);
		System.out.println(valueStack2);
		
		return SUCCESS; 
	}
}
<struts>
	<!-- 配置開發者模式 -->
	<constant name="struts.devMode" value="true"></constant>
	<package name="p1" extends="struts-default">
	
		<action name="demo" class="cn.itcast.valueStack.DemoAction" method="demo">
			<result name="success">/demo.jsp</result>
		</action>
	</package>
</struts>

在這裏插入圖片描述

【值棧小結】:
值棧包括兩部分:root棧和map棧
1、root棧:繼承了List接口,又稱之爲對象棧
2、map棧:實現了Map接口,又可以稱之爲上下文棧(context)
在這裏插入圖片描述


3.值棧的操作(重點)

3.1 Root棧的存值和取值

Root棧的存值:
它按照先進後出的原則存儲數據,即先進入的數據被壓入棧底,最後進入的數據在棧頂,需要讀取數據的時候,從棧頂開始彈出數據(即最後一個數據被第一個讀出來)。
棧也被成爲先進後出表,可進行插入和刪除操作,插入稱之爲進棧(壓棧)(push),刪除稱之爲退棧(pop)

【示例】
值棧數據的保存:向root棧存入數據,響應頁面根據不同的方式(通過ognl表達式)分別將值獲取出來。

1.Push方式壓入棧頂
push方法:將數據壓入root棧的棧頂。

public class DemoAction extends ActionSupport{
	@Override
	public String demo(){
		//獲取值棧的第一種方式
		ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
		//獲取值棧的第二種方式
		ValueStack valueStack2 = ActionContext.getContext().getValueStack();
		System.out.println(valueStack1);
		System.out.println(valueStack2);
		
		//將數據存入root棧,以push方法壓入棧頂
		valueStack2.push("zhangsan");
		//繼續將一個數據壓入棧頂
		valueStack2.push("lisi");
		
		return SUCCESS;
	}
}

Root棧分析:
在這裏插入圖片描述
步驟二:設置響應頁面利用ognl表達式從值棧取值
導入struts標籤:

<%@taglib uri="/struts-tags" prefix="s" %>
demo.jsp頁面內容:
<!-- 
		通過s:property標籤獲取數據 
		value:是一個ognl表達式,通過valu值可以到值棧中去查詢對應的數據
		[0].top:表示獲取root棧棧頂的數據
	-->
	獲取棧頂數據1:<s:property value="[0].top"/><br>
	獲取棧頂數據2:<s:property/><br>
	<s:debug></s:debug>
	執行成功!

步驟三:測試
在這裏插入圖片描述

2.Set方式壓入棧頂
set方法:
將數據壓入棧頂:會先創建一個map集合,讓後將數據存入到集合中,最後將map集合壓入棧頂。

步驟一:修改DemoAction :
public class DemoAction extends ActionSupport{
	
	public String demo(){
		//第一種獲取值棧的方式
		ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
		System.out.println(valueStack1);
		//第二種獲取值棧的方式
		ValueStack valueStack2 = ActionContext.getContext().getValueStack();
		System.out.println(valueStack2);
		
		//將數據存入root棧棧頂
		valueStack2.push("zhangsan");
		valueStack2.push("lisi");
		
		//set:創建一個map集合,將數據存入該集合中,最終將map集合壓入root棧棧頂
		valueStack2.set("name","wangwu");
		
		return SUCCESS;//相當於"none",表示不響應任何結果集視圖
	}
	
}
<%--
		如果棧頂是map集合:通過集合的key可以直接獲取value值
		通過name到值棧中從上往下進行查找,找到的第一個進行返回
	 --%>
	 獲取棧頂map中的數據:<s:property value="name"/><br>

3.通過action的成員變量將數據存入值棧
頁面的<s :debug>標籤會通過getName()方法從值棧中獲取數據並顯示在root棧中
步驟一:修改ValueStackAction
在action中添加成員變量,並設置成員變量的的getter和setter方法


3.2 Map棧的存值和取值

1.通過put方法將數據存入map棧

put方法:
將數據存入map棧,map棧是一個map,因此需要通過key來獲取對應的value.
如果需要直接從map棧獲取數據,建議在ognl表達式中添加一個#,表示直接從map棧查找,而不是從root棧棧頂開始從上往下查找。

如:<s:property value="#name"/>

修改DemoAction

public class DemoAction extends ActionSupport{
	private String name = "maliu";
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String demo(){
		//第一種獲取值棧的方式
		ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
		System.out.println(valueStack1);
		//第二種獲取值棧的方式
		ValueStack valueStack2 = ActionContext.getContext().getValueStack();
		System.out.println(valueStack2);
		
		//將數據存入root棧棧頂
		valueStack2.push("zhangsan");
		valueStack2.push("lisi");
		
		//set:創建一個map集合,將數據存入該集合中,最終將map集合壓入root棧棧頂
		valueStack2.set("name","wangwu");

		//將數據存入map棧
		//由於map棧本質就是一個集合,因此存儲方式和map一樣
		ActionContext.getContext().put("name", "tianqi");
		return SUCCESS;//相當於"none",表示不響應任何結果集視圖
	}
}

頁面獲取map棧數據

<%--
		從map棧獲取數據:在ognl表達式中使用#表示直接從map棧查找數據
	 --%>
	 
	 獲取map棧中的數據:<s:property value="#name"/>

3.3 值棧的存取小結

值棧的主要作用就是數據的保存和獲取
使用值棧的時候要思考:用什麼代碼存進去,然後再用什麼代碼取出來

1.如何向值棧保存數據

  • ValueStack.push(obj) :保存數據到Root棧頂-壓入棧頂
  • ValueStack.set(key,value):將數據保存到Root棧頂(數據被封裝到map中,然後將map壓入棧頂)–有key (不推薦)
  • 提供Action成員變量,提供setter和getter方法(Action就在root棧中,Action屬性可以被搜索)
  • ActionContext.getContext().put(key,value) :保存數據到Map棧中—有key

2.如何向值棧保存數據

  • JSP頁面獲取

    • 通過 [index].top 指定訪問root棧某個對象 ,例如<s:property value=”[0].top”/> ,獲取棧頂對象的簡寫方式:<s:property/>
    • <s :property value= “name”/> 先搜索root棧的key,再搜索map的key ,將查找到的第一個數據進行返回 (不推薦)
    • <s:property value=”#name” /> 搜索map的key ,獲取map棧中的數據
  • Action代碼獲取
    ValueStack.findValue(ognl表達式) ; 獲取值棧數據

//代碼怎麼獲取值棧數據(頁面的ognl表達式獲取數據的底層代碼)
String str1=(String)ActionContext.getContext().getValueStack().findValue("username");
System.out.println(str1);
String str2=(String)ActionContext.getContext().getValueStack().findValue("#username");
System.out.println(str2);

值棧的主要目的:就是存進去,取出來。


3.4 值棧的創建過程源代碼分析(瞭解)

值棧(和保存對象)的創建過程詳解:

1.訪問action的時候,走過濾器,StrutsPrepareAndExecuteFilter中創建了值棧(ValueStack<–OgnlValueStack),並將常用web對象保存到OgnlContext的Map中。
在這裏插入圖片描述
注意:此時root棧爲空。

2.當訪問Action的時候,過濾器會通過ActionProxy代理對象來執行。通過ActionProxyFactory的createActionProxy方法來得到代理對象。在獲取代理對象的時候,需要創建一個ActionInvocation接口的默認實現類DefaultActionInvocation的對象,該默認對象在創建的時候會自動調用init方法來初始化,在初始化方法中會自動調用stack.push(action)方法,將當前Action對象,壓入Root棧的頂部。
在這裏插入圖片描述
注意:再通過debug調試發現,默認情況下ValueStack的root棧存在2個對象 ,ValueStackAction(Action在棧頂)和DefaultTextProvider(用來初始化資源信息)

原理目的:

  • 知道值棧的初始化時機,訪問action的時候,才創建(這個過程中會創建actionProxy對象,並且同時創建值棧,並且,將一些對象放入值棧。)
  • 值棧初始化之後,裏面主要默認有:root棧(action類對象、text。。、model(user)),map棧(servlet相關對象、action對象一個引用)
  • 要了解:哪些對象在棧頂!因爲後面我們從值棧取值,都從棧頂往下取。所以,一定要知道哪個對象在棧上面。

3.5 值棧的生命週期

【回顧分析】:

  • 貫穿整個Action的生命週期,每個Action類的對象實例都擁有一個ValueStack對象。
  • Struts2框架將ValueStack對象保存在名爲“struts.valueStack”的請求(request)屬性中,即值棧是request中的一個對象,一個請求對應一個Action實例和一個值棧對象。

值棧能夠線程安全的爲每個請求提供公共的數據存取服務。當有請求到達的時候,Struts2會爲每個請求創建一個新的值棧,也就是說,值棧和請求是一一對應的,不同的請求,值棧也不一樣,而值棧封裝了一次請求所有需要操作的相關的數據。
因此:值棧的生命週期,就是request生命週期。

問題:如果結果集使用的是重定向,值棧中的數據存在嗎?
答案:值棧存在於request中,request在一次請求結束後生命週期也隨之結束,因此值棧的生命週期也就結束了,所以重定向之後的值棧是第二次請求發送時創建的一個新的值棧,找不到之前的數據了。


4.Struts2使用表達式語言來操作值棧

4.1 Struts2支持的表達式語言

Struts 2支持以下幾種表達式語言:

  • OGNL(Object-Graph Navigation Language),可以方便地操作對象屬性的開源表達式語言;
  • EL(Expression Language),可以方便的操作JSP頁面四個域範圍數據 (page、 request、 session、 application );
  • JSTL(JSP Standard Tag Library),JSP 2.0集成的標準的表達式語言;
  • Groovy,基於Java平臺的動態語言,它具有時下比較流行的動態語言(如Python、Ruby和Smarttalk等)的一些起特性;
  • Velocity,嚴格來說不是表達式語言,它是一種基於Java的模板匹配引擎,具說其性能要比JSP好。

Struts 2默認的表達式語言是OGNL,原因是它相對其它表達式語言更簡單、強大,最重要的是可以直接操作值棧。


4.2 OGNL對Struts2的值棧的操作方式

Struts2 存取數據,通過值棧完成的, Ognl是訪問值棧數據的表示式語言
在Struts2中,使用OGNL表達式獲取數據有三種用法 :

  • # 號用法:獲取map棧數據
  • % 號用法:強制解析或者不解析
  • $ 號用法:xml中獲取值棧數據

1.#號的使用
在使用#獲取map棧數據之前,我們對map棧的結構可以在進行一次深入的理解,map棧除了我們可以手動存儲數據之外,還保存了其他的一些數據,主要如下:

Map的key(類型是String) Map的Value (類型是Object) 說明信息
application Java.util.Map<String,Object> 封裝的應用域中的所有數據
session Java.util.Map<String,Object> 封裝的會話域中的所有數據
request Java.util.Map<String,Object> 封裝的請求域中的所有數據
parameters Java.util.Map<String,String[]> 封裝的是請求參數
attr Java.util.Map<String,Object> 封裝的是四大域的組合數據,從最小的域開始搜索

那麼如何來獲取map棧中的各大域的數據呢?

用法一: 訪問Map棧中常用對象,包括web對象, 添加#進行訪問
在這裏插入圖片描述
提示:#attr 按照 page — request — session — application的順序依次進行搜索

編寫action

public class Demo2Action extends ActionSupport{
	
	public String demo2(){
		//將數據存入request
		ServletActionContext.getRequest().setAttribute("name", "request_name");
		//將數據存入session
		ServletActionContext.getRequest().getSession().setAttribute("name", "session_name");
		//將數據存入application
		ServletActionContext.getServletContext().setAttribute("name", "application_name");
		return SUCCESS;
	}
}

Struts.xml

<action name="demo2" class="cn.itcast.expr.Demo2Action" method="demo2">
	<result name="success">/demo2.jsp</result>
</action>

創建頁面

<body>
  內容:
  參數範圍:<s:property value="#parameters.name"/>|${param.name }<br>
  request範圍:<s:property value="#request.name"/>|${requestScope.name }<br>
  session範圍:<s:property value="#session.name"/>|${sessionScope.name }<br>
  application範圍:<s:property value="#application.name"/>|${applicationScope.name }<br>
  page範圍:<s:property value="#attr.name"/>|${name }<br>
</body>

顯示如下:
在這裏插入圖片描述
注意:attr依次從page --- request --- session --- application的順序依次進行搜索。


2.%號的使用
%主要作用是控制解析或者不解析

Struts標籤分爲普通標籤和表單標籤兩種:
普通標籤是通過屬性value的ognl表達式獲取值棧數據的,比如:<s:property value=””>
表單標籤是通過name屬性的ognl表達式獲取值棧數據的,value屬性只能存入字符串,比如:<s:textfield value=“username”>

%”符號的用途是在標籤的屬性值被理解爲字符串類型時,告訴執行環境%{}裏的是OGNL表達式,就比如:<s:textfield value=”%{username}”>。 也可在OGNL表達式中,添加%{‘ ’}或者’’,來讓其變成普通字符串而不解析,比如:<s:property value=”%{‘username}’”>
簡單的說:%表達式的作用是,可以讓字符串變成支持ognl表達式的解析,也可以讓解析的表達式變成字符串,

public class Demo3Action extends ActionSupport{
	
	public String demo3(){
		ActionContext.getContext().put("name", "zhangsan");
		return SUCCESS;
	}
}
<action name="demo3" class="cn.itcast.expr.Demo3Action" method="demo3">
	<result name="success">/demo3.jsp</result>
</action>

demo3.jsp頁面

創建一個demo3.jsp頁面:
<body>
	<!-- 獲取map棧數據 -->
	<s:property value="#name"/><br>
	<!-- 
		強制解析
		s:textfield:該標籤的value屬性並不支持ognl表達式,爲了能夠獲取數據就需要強制解析成一個ognl表達式
	 -->
	<s:textfield value="%{#name}"></s:textfield><br>
	<!-- 
		不解析
	 -->
	 <s:property value="%{'name'}"/><br>
</body>

在這裏插入圖片描述

3.$號的使用
$主要作用是在相關配置文件中引入OGNL表達式,讓其在配置文件中也能解析OGNL表達式。(換句話說:$用於在配置文件中獲取值棧的值用的。)

需求:通過結果集類型重定向到另外一個頁面的時候,在第二次發送的請求地址後面拼接一個從值棧中獲取到的數據。

步驟一:編寫action

public class Demo4Action extends ActionSupport{
	
	public String demo4() throws UnsupportedEncodingException{
		ActionContext.getContext().put("name", "lisi");
		return SUCCESS;
	}

}

步驟二:struts.xml:

<action name="demo4" class="cn.itcast.expr.Demo4Action" method="demo4">
	<result name="success" type="redirect">/demo4.jsp?name=${#name}</result>
</action>

jsp頁面

新建一個demo4.jsp頁面
<body>
	<h1>執行成功!</h1>
</body>

在這裏插入圖片描述

4.3 EL表達式獲取值棧的數據

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章