ognl與valueStack
ognl中有一個OgnlContext,它可以設(shè)置root與非root .root中數(shù)據(jù)獲取時,不需要加#,而非root中數(shù)據(jù)在獲取時扳缕,需要加上#.
重點:學(xué)習(xí)struts2中使用ognl時忌警,最后要知道 誰是OgnlContext,誰是root瓦宜,誰是非root.
ognl
OGNL是Object Graphic Navigation Language(對象圖導(dǎo)航語言)的縮寫筐喳,它是一個開源項目姑原。 Struts2框架使用OGNL作為默認(rèn)的表達式語言悬而。
* xwork 提供 OGNL表達式
* ognl-3.0.5.jar
OGNL 是一種比EL 強大很多倍的語言
OGNL 提供五大類功能
1、支持對象方法調(diào)用锭汛,如xxx.doSomeSpecial()笨奠;
2、支持類靜態(tài)的方法調(diào)用和值訪問
3唤殴、訪問OGNL上下文(OGNL context)和ActionContext般婆; (重點 操作ValueStack值棧 )
4、支持賦值操作和表達式串聯(lián)
5眨八、操作集合對象腺兴。
演示:在struts2中使用ognl表達式
需要結(jié)合struts2的標(biāo)簽使用<s:property value="ognl表達式">
<s:property value="'abc'.length()"/> 演示對象調(diào)用方法
<s:property value="@java.lang.Math@max(10,20)"/> 演示靜態(tài)成員訪問.
注意:在struts2中使用靜態(tài)成員訪問,必須設(shè)置一個常量:
struts.ognl.allowStaticMethodAccess=false
ValueStack
它是一個接口com.opensymphony.xwork2.util.ValueStack廉侧。
我們使用它是將其做為一個容器页响,用于攜帶action數(shù)據(jù)到頁面.
在在頁面上通過ognl表達式獲取數(shù)據(jù)。
問題1:什么是valueStack?
valueStack主要是將action數(shù)據(jù)攜帶到頁面上段誊,通過ognl獲取數(shù)據(jù)
1.ValueStack有一個實現(xiàn)類叫OgnlValueStack.
2.每一個action都有一個ValueStack.(一個請求闰蚕,一個request,一個action,一個valueStack) valueStack生命周期就是request生命周期连舍。
3.valueStack中存儲了當(dāng)前action對象以及其它常用web對象(request,session,application.parameters)
4.struts2框架將valueStack以“struts.valueStack”為名存儲到request域中没陡。
問題2:valueStack結(jié)構(gòu)?
ValueStack中 存在root屬性 (CompoundRoot) 、 context 屬性 (OgnlContext )
* CompoundRoot 就是ArrayList
* OgnlContext 就是 Map
list集合中存儲的是action相關(guān)信息
map集合中存儲的是相關(guān)映射信息索赏,包含 paramters,request,session,application attr等盼玄。
我們想要從list中獲取數(shù)據(jù),可以不使用#號.(它就是ognl的root)
如果從map中獲取數(shù)據(jù)潜腻,需要使用#. (其實在struts2中的map--context其實就是ognlContext)
結(jié)論:
ValueStack它有兩部分 List Map
在struts2中List就是root Map就是ognlContext.
默認(rèn)情況下埃儿,在struts2中從valueStack獲取數(shù)據(jù)從root中獲取。
valueStack內(nèi)部結(jié)構(gòu)
問題3: 值棧對象的創(chuàng)建 融涣,ValueStack 和 ActionContext 是什么關(guān)系 童番?
ActionContext ctx = ActionContext.getContext();
if (ctx != null) {
stack = ctx.getValueStack();
}
valueStack是每一次請求時,都會創(chuàng)建.
在ActionContext中持有了valueStack的引用威鹿。
問題4:如何獲得值棧對象?
對于valueStack獲取有兩種方式:
1.通過 request獲取
ValueStack vs=(ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
2.通過ActionContext獲取.
ValueStack vs=ActionContext.getContext().getValueStack();
問題5:向值棧保存數(shù)據(jù) (主要針對 root)
主要有兩個方法
push(Object obj)------->底層就是 root.add(0,obj) 將數(shù)據(jù)存儲到棧頂剃斧。
set(String name,Object obj);----->底層是將數(shù)據(jù)封裝到HashMap中,在將這個HashMap通過push存儲忽你。
在jsp中 通過 <s:debug /> 查看值棧的內(nèi)容
問題6: 在JSP中獲取值棧的數(shù)據(jù)
root中數(shù)據(jù)不需要#幼东,而context中數(shù)據(jù)需要#
1.如果棧頂是一個Map集合,獲取時,可以直接通過Map集合的key來獲取value.
<s:property value="username"/>
2.如果棧頂數(shù)據(jù)不是一個Map筋粗,沒有key值策橘,可以使用序號來獲取。
<s:property value="[0]"> 從0的位置向下查找所有娜亿。
<s:property value="[0].top"> 只查找0位置上數(shù)據(jù)丽已。
如果獲取OgnlContext中數(shù)據(jù):
1.request數(shù)據(jù) request.setAttribute()
2.session數(shù)據(jù) session.setAttribute()
3.application數(shù)據(jù) application.setAttribute()
4.attr 依次從request,session.application中查找
5.parameters 獲取請求參數(shù)
ValueStack主流應(yīng)用:就是解決將action數(shù)據(jù)攜帶到j(luò)sp頁面。
問題:action向jsp攜帶數(shù)據(jù),都是什么樣的數(shù)據(jù)?
1.文本(字符串)
1.fieldError 校驗數(shù)據(jù)錯誤信息提示
2.actionError 關(guān)于邏輯操作時錯誤信息(例如登錄失敗)
3.message 就是一個信息.
this.addFieldError("msg", "字段錯誤信息");
this.addActionError("Action全局錯誤信息");
this.addActionMessage("Action的消息信息");
* fieldError 針對某一個字段錯誤信息 (常用于表單校驗)买决、actionError (普通錯誤信息沛婴,不針對某一個字段 登陸失敗)、 actionMessage 通用消息
在jsp中使用 struts2提供標(biāo)簽 顯示消息信息
<s:fielderror fieldName="msg"/>
<s:actionerror/>
<s:actionmessage/>
2.復(fù)雜數(shù)據(jù)
可以使用valueStack存儲.
在action中存儲數(shù)據(jù):
List<User> users = new ArrayList<User>();
users.add(new User("tom", "123", 20, "男"));
users.add(new User("james", "456", 21, "男"));
users.add(new User("fox", "789", 26, "男"));
vs.push(users);
在頁面上獲取數(shù)據(jù):
使用了<s:iterator>標(biāo)簽來迭代集合督赤。
<s:iterator value="[0].top" var="user"> 這是將集合中迭代出來每一個元素起個名稱叫user,而user是存儲在context中嘁灯,不在root中.l
<s:iterator value="[0].top" var="user">
username:<s:property value="#user.username"/><br>
password:<s:property value="#user.password"/>
<hr>
</s:iterator>
注意:如果我們在使用<s:iterator>進行迭代時,沒有給迭代出的元素起名.
<s:iterator value="[0].top">
username:<s:property value="username"/><br>
password:<s:property value="password"/>
<hr>
</s:iterator>
關(guān)于默認(rèn)壓入到valueStack中的數(shù)據(jù).
1.訪問的action對象會被壓入到valueStack中.
DefaultActionInvocation 的 init方法 stack.push(action);
* Action如果想傳遞數(shù)據(jù)給 JSP躲舌,只有將數(shù)據(jù)保存到成員變量丑婿,并且提供get方法就可以了
2.ModelDriveInterceptor會執(zhí)行下面操作
ModelDriven modelDriven = (ModelDriven) action;
ValueStack stack = invocation.getStack();
Object model = modelDriven.getModel();
if (model != null) {
stack.push(model);
}
將實現(xiàn)了ModelDrive接口的action中g(shù)etModel方法的返回值,也就是我們所說的model對象壓入到了
valueStack.
模型驅(qū)動modle壓入值棧調(diào)用問題
相關(guān)件結(jié)構(gòu)
問題7:為什么el表達式可以訪問valueStack中數(shù)據(jù)没卸?
struts2框架中所使用的request對象羹奉,是增強后的request對象。
${username}---->request.getAttribute("username");
增強后的request,會首先在request域范圍查找约计,如果查找不到诀拭,會在valueStack中查找。
StrutsPreparedAndExecuteFilter的doFilter代碼中 request = prepare.wrapRequest(request);
* 對Request對象進行了包裝 煤蚌,StrutsRequestWrapper
* 重寫request的 getAttribute
Object attribute = super.getAttribute(s);
if (attribute == null) {
attribute = stack.findValue(s);
}
訪問request范圍的數(shù)據(jù)時耕挨,如果數(shù)據(jù)找不到,去值棧中找
request對象 具備訪問值棧數(shù)據(jù)的能力 (查找root的數(shù)據(jù))
OGNL表達式常見使用($ % #)
1.#號
用法一 # 代表 ActionContext.getContext() 上下文
<s:property value="#request.name" /> ------------> ActionContext().getContext().getRequest().get("name");
#request
#session
#application
#attr
#parameters
用法二 : 不寫# 默認(rèn)在 值棧中root中進行查找
<s:property value="name" /> 在root中查找name屬性
* 查詢元素時尉桩,從root的棧頂元素 開始查找筒占, 如果訪問指定棧中元素
<s:property value="[1].name" /> 訪問棧中第二個元素name屬性
* 訪問第二個元素對象 <s:property value="[1].top" />
用法三 :進行投影映射 (結(jié)合復(fù)雜對象遍歷 )
1)集合的投影(只輸出部分屬性
<h1>遍歷集合只要name屬性</h1>
<s:iterator value="products.{name}" var="pname">
<s:property value="#pname"/>
</s:iterator>
2)遍歷時,對數(shù)據(jù)設(shè)置條件
<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>
用法四: 使用#構(gòu)造map集合
經(jīng)常結(jié)合 struts2 標(biāo)簽用來生成 select蜘犁、checkbox赋铝、radio
<h1>使用#構(gòu)造map集合 遍歷</h1>
<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.%號
%作用:就是用于設(shè)定當(dāng)前是否要解析其為 ognl表達式.
%{表達式} 當(dāng)前表達式會被做為ognl解析.
%{'表達式'} 當(dāng)前表達式不會被做為ognl解析。
<s:property value="表達式"> 對于s:property標(biāo)簽沽瘦,它的value屬性會被默認(rèn)做為ognl.
以后,所有表達式如果想要讓其是ognl %{表達式}
3.$號
$作用:就是在配置文件中使用ognl表達式來獲取valueStack中數(shù)據(jù).
1.struts.xml
<result type="stream">
<param name="contentType">${contentType}</param>
</result>
2.在校驗文件中使用
${min} ${max}
${minLength} ${maxLength}
3.在國際化文件中使用
在properties文件中
username=${#request.username}
在jsp頁面
<s:text name="username">
總結(jié): #就是用于獲取數(shù)據(jù) %就是用于設(shè)置是否是ognl表達式 $就是在配置文件中使用ognl.
防止表單重復(fù)提交
問題:什么是表單重復(fù)提交农尖?
regist.jsp----->RegistServlet
表單重復(fù)提交 危害: 刷票析恋、 重復(fù)注冊、帶來服務(wù)器訪問壓力(拒絕服務(wù))
解決方案:
在頁面上生成一個令牌(就是一個隨機字符串),將其存儲到session中盛卡,并在表單中攜帶.
在服務(wù)器端助隧,獲取數(shù)據(jù)時,也將令牌獲取,將它與session中存儲的token對比并村,沒問題巍实,
將session中令牌刪除。
struts2中怎樣解決表單重復(fù)提交:
在struts2中解決表單重復(fù)提交哩牍,可以使用它定義的一個interceptor棚潦。
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
步驟:
1.在頁面上需要使用一個token tag
在表單中添加一個標(biāo)簽 <s:token/>
就會在頁面上生成一個令牌,并存在于表單中膝昆。
2.需要在action中引入token攔截器
<interceptor-ref name="token"/>
3.需要配置視圖
<result name="invalid.token">/token.jsp</result>
通過 <s:actionError/> 顯示錯誤信息
覆蓋重復(fù)提交信息 struts.messages.invalid.token=您已經(jīng)重復(fù)提交表單丸边,請刷新后重試
struts2中json插件使用
1.struts2中怎樣處理異步提交(ajax)
原始:
HttpServletResponse response = ServletActionContext.getResponse();
response.getWriter().write("hello " + msg);
response.getWriter().close();
還可以使用struts2中提供的json插件:
1.導(dǎo)入json插件包
在struts2的lib包下 struts2-json-plugin-2.3.15.1.jar。
2.在struts.xml文件中配置
1.<package extends="json-default">
2.設(shè)置視圖<result type="json">
這樣設(shè)置后荚孵,會將valueStack棧頂數(shù)據(jù)變成json妹窖。
對于我們的程序,也就是會將action對象轉(zhuǎn)換成json收叶。
<param name="root">p</param> 如果沒有設(shè)置骄呼,可以理解成將整個action都轉(zhuǎn)換成json的數(shù)據(jù)。也就是
在action中提供的getXxx方法判没,就是json中的一個屬性蜓萄。
如果設(shè)置了root,那么,只將指定數(shù)據(jù)轉(zhuǎn)換成json.
怎樣設(shè)置轉(zhuǎn)換成json的對象中不包含特定的屬性?
1. @JSON(serialize=false) 在getXxx方法上設(shè)置
2. 還可以通過json插件的interceptor完成.
<param name="includeProperties">ps\[\d+\]\.name,ps\[\d+\]\.price,ps\[\d+\]\.count</param>