1. 程式人生 > >Struts2入門(三)

Struts2入門(三)

Struts2第三天


課程回顧:Struts2框架的第二天

1. Servlet的API
	* ActionContext物件
	* ServletActionContext物件

2. 結構型別的跳轉
	* 全域性結果
	* 區域性結構,type屬性

3. 資料的封裝
	* 屬性驅動方式
	* 模型驅動方式

4. 攔截器(自定義攔截器)

今天的課程內容

1. OGNL表示式(瞭解)
2. Struts2框架的值棧(值棧、存入值、取值)
3. OGNL的特殊符號

案例一:使用Struts2框架查詢所有的客戶功能


需求分析

1. 使用Struts2框架查詢所有的客戶功能

技術分析之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>