OGNL表示式使用 和 值棧
OGNL是Object Graphic Navigation Language(對象圖導航語言)的縮寫,它是一個開源項目。 Struts2框架使用OGNL作為默認的表達式語言。
* xwork 提供 OGNL表達式
* ognl-3.0.5.jar
OGNL 是一種比EL 強大很多倍的語言
OGNL 提供五大類功能
1、支持對象方法調用,如xxx.doSomeSpecial();
2、支持類靜態的方法調用和值訪問
3、訪問OGNL上下文(OGNL context)和ActionContext; (重點 操作ValueStack值棧 )
4、支持賦值操作和表達式串聯
5、操作集合對象。
1、 使用OGNL訪問 對象方法 和 靜態方法
* OGNL 在jsp 結合 struts2 標簽庫 使用 , <s:property value="ognl表達式" /> 執行 ognl表達式
調用 實例方法 : 對象.方法() ---- <s:property value="'hello,world'.length()"/>
調用 靜態方法 : @[類全名(包括包路徑)]@[方法名] --- <s:property value="@java.lang.String@format('您好,%s','小明')"/>
* 使用 靜態方法調用 必須 設置 struts.ognl.allowStaticMethodAccess=true
2、 訪問OGNL上下文(OGNL context)和ActionContext
OGNL上下文(OGNL context) 對象 ----- 值棧 ValueStack
什么是值棧 ValueStack ?
值棧:簡單的說,就是存放action的堆棧,當我們提交一個請求到服務器端 action時,就有個堆棧,如果action在服務器端進行跳轉,所有action共用一個堆棧,當需要保存在action中的數據時,首先從棧頂開始 搜索,若找到相同的屬性名(與要獲得的數據的屬性名相同)時,即將值取出,但這種情況可能出現找到的值不是我們想要的值,那么解決此問題需要用TOP語法 和N語法來進行解決。
0
ValueStack 是 struts2 提供一個接口,實現類 OgnlValueStack ---- 值棧對象 (OGNL是從值棧中獲取數據的 )
每個Action實例都有一個ValueStack對象 (一個請求 對應 一個ValueStack對象 )
在其中保存當前Action 對象和其他相關對象 (值棧中 是有Action 引用的 )
Struts 框架把 ValueStack 對象保存在名為 “struts.valueStack” 的請求屬性中,request中 (值棧對象 是 request一個屬性)
valueStack vs = request.getAttribute("struts.valueStack");
問題二 : 值棧的內部結構 ?
值棧由兩部分組成
ObjectStack (root): Struts 把動作和相關對象壓入 ObjectStack 中--List
ContextMap (context): Struts 把各種各樣的映射關系(一些 Map 類型的對象) 壓入 ContextMap 中
Struts 會把下面這些映射壓入 ContextMap 中
parameters: 該 Map 中包含當前請求的請求參數 ?name=xxx
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 元素
問題三 : 值棧對象的創建 ,ValueStack 和 ActionContext 是什么關系 ?
值棧對象 是請求時 創建的
doFilter中 prepare.createActionContext(request, response);
* 創建ActionContext 對象過程中,創建 值棧對象ValueStack
* ActionContext對象 對 ValueStack對象 有引用的 (在程序中 通過 ActionContext 獲得 值棧對象 )
Dispatcher類 serviceAction 方法中 將值棧對象保存到 request范圍
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
問題四 : 如何獲得值棧對象
獲得值棧對象 有兩種方法
ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
ValueStack valueStack2 = ActionContext.getContext().getValueStack();
問題五: 向值棧保存數據 (主要針對 root)
兩種方式
// 將數據保存root的索引0位置,放置到第一個元素 ArrayList add(0,element);
valueStack.push("itcast");
// 在值棧創建參數map, 將數據保存到map中
valueStack.set("company", "數據");
在jsp中 通過 <s:debug /> 查看值棧的內容
問題六: 在JSP中獲取值棧的數據
訪問root中數據 不需要#
訪問 其它對象數據 加 #
通過下標獲取root中對象
<s:property value="[0].top"/> //取值棧頂對象
直接在root中查找對象屬性 (自上而下自動查找)
valueStack:<s:property value="username"/>
在OgnlContext中獲取數據
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[0]"/>
valueStack對象 保存在request范圍, 值棧生命周期 就是 request的生命周期 ,值棧的內部結構(root、ognlContext)
3、 值棧在開發中應用
主流應用 : 值棧 解決 Action 向 JSP 傳遞 數據問題
Action 向JSP 傳遞數據處理結果 ,結果數據有兩種形式
1) 消息 String類型數據
this.addFieldError("msg", "字段錯誤信息");
this.addActionError("Action全局錯誤信息");
this.addActionMessage("Action的消息信息");
* fieldError 針對某一個字段錯誤信息 (常用於表單校驗)、actionError (普通錯誤信息,不針對某一個字段 登陸失敗)、 actionMessage 通用消息
在jsp中使用 struts2提供標簽 顯示消息信息
<s:fielderror fieldName="msg"/>
<s:actionerror/>
<s:actionmessage/>
2) 數據 (復雜類型數據)
使用值棧 valueStack.push(products);
哪些數據默認會放入到值棧 ???
1)每次請求,訪問Action對象 會被壓入值棧 ------- DefaultActionInvocation 的 init方法 stack.push(action);
* Action如果想傳遞數據給 JSP,只有將數據保存到成員變量,並且提供get方法就可以了
2)ModelDriven 接口 有一個單獨攔截器
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
在攔截器中 ,將model對象 壓入了 值棧 stack.push(model);
user.usernae ---------------?><input name="username"/>
* 如果Action 實現ModelDriven接口,值棧默認棧頂對象 就是model對象
4、 值棧的數據 通過EL訪問
問題七:為什么 EL也能訪問值棧中的數據
ActionContext.getContext().getSession().setAttribute("key","value");
${requestScope.username}----------->requst.getAttribute("username");------>重寫getAttribute()方法,沒有找到數據的情況下,繼續找ValueStack
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的數據)
5、 OGNL表達式 常見使用
#、 % 、$ 符號使用
1) # 的 使用
用法一 # 代表 ActionContext.getContext() 上下文
<s:property value="#request.name" /> ------> ActionContext().getContext().getRequest().getAttribute("name");
#request
#session
#application
#attr
#parameters
用法二 : 不寫# 默認在 值棧中root中進行查找
<s:property value="name" /> 在root中查找name屬性
* 查詢元素時,從root的棧頂元素 開始查找, 如果訪問指定棧中元素 <s:property value="[1].top.name" /> 訪問棧中第二個元素name屬性
* 訪問第二個元素對象 <s:property value="[1].top" />
用法三 :進行投影映射 (結合復雜對象遍歷 )
s:iterator標簽在進行遍歷時,會將var中保存的這個對象,在root中,和context中都放一份
所以在<s:property value=""/> value屬性加不加#都可以訪問
1)集合的投影(只輸出部分屬性
<h1>遍歷集合只要name屬性</h1>
<s:iterator value="products.{name}" var="pname">
<s:property value="#pname"/>
</s:iterator>
2)遍歷時,對數據設置條件
<h1>遍歷集合只要price大於1500商品</h1>
<s:iterator value="products.{?#this.price>1500}" var="product">
<s:property value="#product.name"/> --- <s:property value="#product.price"/>
</s:iterator>
3)綜合
<h1>只顯示價格大於1500 商品名稱</h1>
<s:iterator value="products.{?#this.price>1500}.{name}" var="pname">
<s:property value="#pname"/>
</s:iterator>
用法四: 使用#構造map集合
經常結合 struts2 標簽用來生成 select、checkbox、radio
<h1>使用#構造map集合 遍歷</h1>
#{key:value,kye1:value1}
<s:iterator value="#{'name':'aaa','age':'20', 'hobby':'sport' }" var="entry">
key : <s:property value="#entry.key"/> , value: <s:property value="#entry.value"/> <br/>
</s:iterator>
2) %的使用
用法一: 結合struts2 表單標簽使用, 通過%通知struts, %{}中內容是一個OGNL表達式,進行解析
<s:textfield name="username" value="%{#request.username}"/>
<s:textfield name="username" value="<s:property value='#request.username'/>" />是錯誤的
用法二: 設置ognl表達式不解析 %{'ognl表達式'}
<s:property value="%{'#request.username'}"/>
3)$的使用
用法一 :用於在國際化資源文件中,引用OGNL表達式
在properties文件 msg=歡迎您, ${#request.username}
在頁面
<s:i18n name="messages">
<s:text name="msg"></s:text>
</s:i18n>
* 自動將值棧的username 結合國際化配置信息顯示
用法二 :在Struts 2配置文件中,引用OGNL表達式
<!-- 在Action 提供 getContentType方法 -->
<param name="contentType">${contentType}</param>
* ${contentType} 讀取值棧中contentType數據,在Action提供 getContentType 因為Action對象會被壓入值棧,
contentType是Action屬性,從值棧獲得
結論: #用於寫ognl表達式獲取數據,% 控制ognl表達式是否解析 ,$ 用於配置文件獲取值棧的數據
五、 struts2 標簽庫
tag-reference.html 就是 struts2 標簽規范
1、 通用標簽庫 的學習
<s:property> 解析ognl表達式,設置默認值,設置內容是否HTML轉義
<s:set> 對比c:set向四個數據范圍保存數據
<s:iterator> 遍歷值棧中數據 <c:forEach />
<s:if> <s:elseif> <s:else> 進行條件判斷 -------- elseif 可以有多個
<s:url> 進行URL重寫(追蹤Session ) ,結合s:param 進行參數編碼
* <s:url action="download" namespace="/" var="myurl">
<s:param name="filename" value="%{'MIME協議簡介.txt'}"></s:param>
</s:url>
<s:property value="#myurl"/>
<s:a> 對一個鏈接 進行參數編碼
* <s:a action="download" namespace="/" >下載MIME協議簡介.txt
<s:param name="filename" value="%{'MIME協議簡介.txt'}"></s:param>
</s:a>
2、 UI標簽庫的學習 (Form標簽)
使用struts2 form標簽 好處 : 支持數據回顯 , 布局排版(基於Freemarker 模板定義 )
struts2 表單標簽 value屬性。 必須寫 %{} 進行設值
使用struts2表單標簽前, 必須配置 StrutsPrepareAndExecuteFilter
<s:form> 表單標簽
<s:form action="regist" namespace="/" method="post" theme="xhtml"> --- theme="xhtml" 默認布局樣式
<s:textfield> 生成 <input type="text" >
<s:password > 生成 <input type="password" >
<s:submit type="submit" value="注冊"/> 生成 <input type="submit" >
<s:reset type="reset" value="重置" /> 生成 <input type="reset" >
* struts2 除了支持JSP之外,支持兩種模板技術 Velocity (擴展名 .vm ) 、 Freemarker (擴展名 .ftl)
<s:textarea> 生成 <textarea> 多行文本框
<s:checkboxlist> 生成一組checkbox
* 使用ognl構造 Map (看到值和提交值 不同時)
* <s:checkboxlist list="#{'sport':'體育','read':'讀書','music':'音樂' }" name="hobby"></s:checkboxlist>
<s:radio> 生成一組radio
* 使用 ognl構造 List (看到內容和提交值 相同時)
* <s:radio list="{'男','女'}" name="gender"></s:radio>
<s:select> 生成一個<select>
* <s:select list="{'北京','上海','南京','廣州'}" name="city"></s:select>
struts2 開發 密碼框 默認不回顯
<s:password name="password" id="password" showPassword="true"/>
3、 頁面元素主題設置
default.properties ---- struts.ui.theme=xhtml 設置struts2 頁面元素使用默認主題
struts.ui.templateSuffix=ftl 默認模板引擎 Freemarker
修改主題
方式一 :<s:textfield name="username" label="用戶名“ theme="simple"></s:textfield> 只對當前元素有效
方式二 :<s:form action="" method="post" namespace="/ui“ theme="simple"> 對form中所有元素有效
方式三 : struts.xml
<constant name="struts.ui.theme" value="simple"></constant> 修改默認主題樣式,頁面所有元素都有效
優先級 : 方式一 > 方式二 > 方式三