上篇文章把OGNL單獨(dú)拿出來講了,這篇文章就講講OGNL結(jié)合Struts2的使用较雕。
Struts2中OGNL表達(dá)式必須配合Struts2標(biāo)簽使用碉哑,不然沒什么效果。
(八)Struts2進(jìn)階之值棧詳解這篇文章中我們分析了值棧的結(jié)構(gòu)亮蒋,講了值棧的實(shí)現(xiàn)類是OgnlValueStack類扣典,它包含兩部分,分別是root(對(duì)應(yīng)CompoundRoot類)和context(對(duì)應(yīng)OgnlContext類)慎玖。
在每次調(diào)用核心控制器時(shí)贮尖,在它的doFilter方法中有這么一行代碼
prepare.createActionContext(request, response);
這行對(duì)于我們理解值棧很關(guān)鍵啊,本來應(yīng)該放在值棧里說的凄吏。远舅。。
首先先捋一捋ActionContext是什么玩意痕钢,上篇文章單純講OGNL的時(shí)候說了图柏,它有一個(gè)執(zhí)行的上下文環(huán)境OgnlContext,它里面有一個(gè)根對(duì)象和若干個(gè)普通對(duì)象任连。
那么在Struts2中蚤吹,要使用OGNL,也得給它一個(gè)上下文環(huán)境随抠,其實(shí)我看網(wǎng)上很多文章說ActionContext是上下文環(huán)境裁着,但我認(rèn)為應(yīng)該是OgnlValueStack類,它里面有這么兩行代碼
CompoundRoot root;
transient Map<String, Object> context;
分別代表值棧的root和context拱她。不過我們可以在ActionContext類中取得值棧對(duì)象二驰。
下面開始說。
1.Struts2中的OGNL
Struts2中的OGNL比單純的OGNL要更強(qiáng)大秉沼,上篇文章中桶雀,我們說了只能有一個(gè)root對(duì)象,而在OGNL中唬复,可以有多個(gè)root對(duì)象矗积。root對(duì)應(yīng)的實(shí)現(xiàn)類是CompoundRoot,該類實(shí)質(zhì)是List敞咧,所有能有多個(gè)root對(duì)象棘捣。在Strtus2中的Root使用的是CompoundRoot對(duì)象,而CompoundRoot繼承了ArrayList休建,所以他可以存儲(chǔ)一系列的對(duì)象乍恐,這些對(duì)象可以看作是OGNL中的root對(duì)象评疗。當(dāng)我們當(dāng)問某個(gè)屬性時(shí),CompoundRootAccessor對(duì)象實(shí)例會(huì)負(fù)責(zé)在CompoundRoot對(duì)象中找到包含我們指定屬性的對(duì)象禁熏。
2.再說ValueStack
對(duì)于用戶的每個(gè)action請(qǐng)求壤巷,Struts2在執(zhí)行相應(yīng)的方法之前,都會(huì)新建一個(gè)valuestack對(duì)象瞧毙,其實(shí)這個(gè)創(chuàng)建的過程就在Struts2的核心控制器里的doFilter方法中胧华,也就是從上面的那行代碼開始的,它會(huì)把request宙彪、session矩动、application、atrr释漆、parameters放入context中悲没。
valueStack的內(nèi)部包含兩個(gè)邏輯部分,一個(gè)叫做Object Stack(root)男图,另一個(gè)叫做Context Map(context)示姿。Struts2將動(dòng)作和相關(guān)對(duì)象壓入Object Stack,把各種各樣的映射關(guān)系(Map類型的對(duì)象)壓入Context Map逊笆。其中的Object Stack中的對(duì)象都相當(dāng)于OGNL中的”root”對(duì)象栈戳,因此對(duì)他們可以直接訪問。如果要訪問Context Map中的對(duì)象难裆,那么就得在OGNL表達(dá)式前面加上”#”符號(hào)子檀。如果沒有加”#”,那么Struts2默認(rèn)會(huì)在Object Stack中進(jìn)行搜索乃戈。
Strut2會(huì)把下面的這些映射關(guān)系壓入到Context Map中:
(1) parameters:這個(gè)Map中包含當(dāng)前請(qǐng)求的請(qǐng)求參數(shù)
(2) request:包含當(dāng)前請(qǐng)求的所有屬性
(3) session:包含當(dāng)前請(qǐng)求的會(huì)話的所有屬性
(4) application:包含當(dāng)前應(yīng)用程序的ServletContext屬性
(5) attr:這個(gè)Map用來按照這個(gè)順序來檢索某個(gè)屬性:request褂痰、session、application
注意:請(qǐng)求參數(shù)總是返回一個(gè)String類型的數(shù)組症虑。比如我們要想知道請(qǐng)求參數(shù)的個(gè)數(shù)缩歪,那么正確的表達(dá)式應(yīng)該是#parameters.count[0],而不是#parameters.count谍憔。
3.使用OGNL獲取值棧中的屬性值
獲取root中的值驶冒,我們不需要加#符號(hào),這和上篇文章是一樣的道理韵卤。
下面看看代碼
新建一個(gè)User類,有username和password兩個(gè)屬性
@SuppressWarnings("serial")
public class User implements Serializable{
private String username;
private String password;
public User() {}
public User(String username, String password) {
super();
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
新建一個(gè)OGNLAction類崇猫,實(shí)現(xiàn)了三個(gè)接口沈条,分別用于獲取request,session和application诅炉,另外還有一個(gè)list蜡歹。
@SuppressWarnings("serial")
public class OGNLAction extends ActionSupport implements RequestAware, SessionAware, ApplicationAware {
// 這里的值會(huì)被放入Object Stack(root)中屋厘,記得提供get方法,不然無法取值
private List<User> s1;
// 這里的值會(huì)被放入Context Map(context)中
private Map<String, Object> applicationMap;
// 這里的值會(huì)被放入Context Map(context)中
private Map<String, Object> sessionMap;
// 這里的值會(huì)被放入Context Map(context)中
private Map<String, Object> requestMap;
@Override
public String execute() throws Exception {
User u1 = new User("liu", "12345");
User u2 = new User("xu", "123456");
User u3 = new User("guo", "520520");
s1 = new ArrayList<User>();
Collections.addAll(s1, u1, u2, u3);
applicationMap.put("users", s1);
sessionMap.put("users", s1);
requestMap.put("users", s1);
return SUCCESS;
}
@Override
public void setApplication(Map<String, Object> application) {
this.applicationMap = application;
}
@Override
public void setRequest(Map<String, Object> request) {
this.requestMap = request;
}
@Override
public void setSession(Map<String, Object> session) {
this.sessionMap = session;
}
public List<User> getS1() {
return s1;
}
public void setS1(List<User> s1) {
this.s1 = s1;
}
}
在struts.xml中配置action
<action name="OGNL" class="com.codeliu.action.OGNLAction">
<result>/index.jsp</result>
</action>
新建一個(gè)index.jsp
<body>
<s:debug></s:debug>
<s:property value="s1[0].username"/> or <s:property value="#request.users[1].username"/>
or <s:property value="#application.users[0].username"/> or <s:property value="#session['users'][0].username"/>
<br>
<!-- 遍歷list中的元素 -->
<s:iterator value="s1" var="user">
<!-- 說明使用var屬性后月而,值放在了context Map中汗洒,要通過#去獲取 -->
<s:property value="#user.username"/> <s:property value="#user.password"/><br>
<!-- 不加#也能獲取到,說明在root中沒有找到父款,回去 context Map中找-->
<s:property value="username"/> <s:property value="password"/><br>
</s:iterator>
<!-- 遍歷applicationMap中的元素 -->
<s:iterator value="#application.users" var="user">
<s:property value="#user.username"/> <s:property value="#user.password"/><br>
</s:iterator>
<!-- 遍歷requestMap中的元素 -->
<s:iterator value="#request.users">
<s:property value="username"/> <s:property value="password"/><br>
</s:iterator>
<!-- 遍歷sessionMap中的元素 -->
<s:iterator value="#session.users">
<s:property value="username"/> <s:property value="password"/><br>
</s:iterator>
</body>
啟動(dòng)tomcat后溢谤,結(jié)果如下
代碼上面的注釋都寫的很清楚,說明什么問題憨攒?
1.除了List世杀,其他的三個(gè)變量的值都放進(jìn)去了Context Map中,要取出來必須加上#肝集。
2.使用遍歷標(biāo)簽瞻坝,如果使用了var屬性,則每次遍歷的值都放入了Context Map中杏瞻,使用#符號(hào)也可以取出來所刀。(不使用也可以,因?yàn)樗趓oot中沒找到捞挥,應(yīng)該就會(huì)去Context Map中找浮创。)
3.action類中,放入root中的數(shù)據(jù)树肃,比如上面的list蒸矛,記得要有相應(yīng)的get方法,否則無法取到值胸嘴。
4.使用#session['users'][0].username也可以獲取到屬性的值
4.調(diào)用靜態(tài)方法和靜態(tài)字段
其實(shí)在Struts2中OGNL調(diào)用靜態(tài)方法雏掠、靜態(tài)字段和上篇文章中講的都一樣。
要注意的是劣像,Struts2默認(rèn)是關(guān)閉調(diào)用靜態(tài)方法的功能乡话,所以要使用,得先打開耳奕。
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
對(duì)于值棧中的靜態(tài)屬性和方法,直接使用它們的名稱即可屋群。
<!-- 調(diào)用Java API的靜態(tài)變量和靜態(tài)方法 -->
<s:property value="@java.lang.Math@PI"/>
<br>
<s:property value="@java.lang.Math@floor(4.3)"/><br>
<!-- 調(diào)用自己寫的類中的靜態(tài)字字段和靜態(tài)方法 -->
<s:property value="myName"/><br>
<s:property value="getMyName()"/>
5.投影和過濾
和上篇文章講的也一樣闸婴。
6.# $ %的使用
“#”的作用:
(1)訪問非root對(duì)象的屬性。例如:#session[“userName”]
(2)對(duì)集合進(jìn)行投影與選擇(具體可看上篇文章)
(3)構(gòu)造對(duì)象(具體可看上篇文章),
“%”的作用:
在標(biāo)簽的屬性值被理解為字符串類型時(shí)烙心,告訴執(zhí)行環(huán)境%{}里的是OGNL表達(dá)式 <s:property value="%{#foobar['foo1']}" />
“$”的作用:
(1)在配置文件中引用OGNL表達(dá)式(訪問Action的屬性)柏靶。
(2)在國(guó)際化資源文件中引用OGNL表達(dá)式(學(xué)習(xí)國(guó)際化時(shí)會(huì)學(xué)到)
7.this指針
在很多編程語(yǔ)言中弃理,都有this指針的概念,它表示調(diào)用當(dāng)前函數(shù)(方法)的對(duì)象屎蜓。那么在OGNL中也有類似的概念痘昌。
我們已經(jīng)學(xué)過,OGNL表達(dá)式是以”.”進(jìn)行串聯(lián)的的一個(gè)串字符串表達(dá)式炬转。這個(gè)表達(dá)式在被執(zhí)行的時(shí)候辆苔,從左到右,每一次計(jì)算都會(huì)返回一個(gè)臨時(shí)的當(dāng)前對(duì)象扼劈,并在此臨時(shí)對(duì)象上再次進(jìn)行調(diào)用驻啤,直到執(zhí)行完畢。這個(gè)臨時(shí)的當(dāng)前變量就存儲(chǔ)在一個(gè)叫做this的變量中荐吵,這個(gè)this變量我們就叫它this指針骑冗。通過使用this指針,我們可以使OGNL更加靈活先煎,更加強(qiáng)大贼涩。
注:使用this指針時(shí),必須在this前面加”#”薯蝎,即this指針必須以“#this”的形式出現(xiàn)遥倦。
例如:group.userList.size().(#this+1).toString()
上篇文章中講投影和過濾的時(shí)候使用了this指針
參考文章
https://blog.csdn.net/xiaokang123456kao/article/details/59483038
https://blog.csdn.net/yu102655/article/details/52182801