8.1 OGNL語法
8.1.1 ActionContext、StackContext、ValueStack關係
ActionContext、StackContext、ValueStack對象與OGNL密切相關,理解三者之間的關係是掌握OGNL語法的根本。
1. ActionContext對象
ActionContext(com.opensymphony.xwork.ActionContext)是Action執行時的上下文,上下文可以看作是一個容器,它存放Action在執行時需要用到的對象,例如,請求的參數(Parameter)、會話(Session)、Servlet上下文(ServletContext)。在每次請求執行Action之前都會創建新的ActionContext。ActionContext是線程安全的,在同一個線程中ActionContext的屬性是唯一的。值得注意的是ActionContext不僅保存了Action在執行時需要用到的數據,本身也可存放數據,例如以下代碼用於將uid變量值保存到ActionContext對象中:
ActionContext.getContext().put("uid", "admin");
保存到ActionContext對象中的數據以後可通過Struts2標籤和EL表達式語言進行訪問。
2. StackContext 對象
Stack Context對象是ActionContext上下文中的一個java.util.Stack 實例。上述提及的請求參數(Parameter)、會話(Session)、Servlet上下文(ServletContext)等對象,實質是存儲在ActionContext上下文中的StackContext對象中。
3. ValueStack對象
Struts2會爲每一次Action的請求創建與之對應的ValueStack,並將所有Action屬性數據存儲到ValueStack中。再將ValueStack暴露給視圖頁面,這樣頁面就可直接訪問後臺處理生成的數據。
ActionContext、StackContext、ValueStack三者之間的關係如圖1.8.1所示:
圖 1.8.1 ActionContext、StackContext、ValueStack關係
假設存在LoginAction控制器,代碼如下:
public class LoginAction
{
private String uid;//封裝用戶名
private String pwd;//封裝密碼
public String execute() throws Exception
{
//獲取ActionContext對象
ActionContext ctx=ActionContext.getContext();
//將uid的值存儲到session中
ctx.getSession().put("uid", this.uid);
//將uid的值存儲到Stack Context中
ctx.put("uid", this.uid);
return "success";
}
//getter、setter代碼省略…
}
並且已經正確將該Action的uid屬性賦值爲scott,pwd屬性賦值爲tiger。則ActionContext、StackContext、ValueStack三者此時的數據存放情況如圖1.8.2所示:
圖1.8.2 LoginAction數據存儲情況
8.1.2 OGNL語法基礎
OGNL是Object-Graph Navigation Language(對象圖導航語言)的縮寫,它是一種功能強大的表達式語言,通過它的簡單一致的表達式語法,可以存取對象的任意屬性,調用對象的方法,遍歷整個對象的結構圖,實現字段類型轉化等功能。
1.訪問OGNL上下文中的數據
ActionContext是Struts2中OGNL的上下文環境,也即向ActionContext中添加的數據(實際存儲在Stack Context中)都可以被OGNL來訪問,而ActionContext又包括request,session,ValueStack等這些對象,所以OGNL也可以訪問存儲在這些對象中的數據。
Value Stack(值棧)是OGNL的根,由於OGNL上下文中的根對象可以直接訪問,所以通過OGNL訪問Value Stack中的數據時不需要使用任何特殊的“標記”,而訪問ActionConext中的其他對象則需要使用“#”標記。在Struts2中提供了<s:property>標籤來訪問OGNL上下文中的數據。
(1) <s:property value=”uid”/>
運行該標籤時,Struts2會在Value Stack中去查找名爲uid項,並輸出其值
(2) <s:property value=”#uid”/>
運行該標籤時,Struts2會在StackContext中去查找名爲uid項,並輸出其值
(3) <s:property value=”#session.uid”/>
運行該標籤時,Struts2會在StackContext中的session作用域中去查找名爲uid項,並輸出其值。
(4) <s:property value=”#attr.uid”/>
運行該標籤時,Struts2會依次在StackContext的page,request,session,application範圍內查找對應的uid中的值。
2.訪問JavaBean屬性
假設存在employee對象作爲OGNL上下文的根對象,對於下面的表達式:
(1) <s:property value=”name”/>
運行該標籤時,Struts2會執行employee對象的getName()方法,對應的Java代碼是:employee.getName()
(2) <s:property value=”address.country”/>
運行該標籤時,Struts2會執行employee對象的getAddress()方法和getCountry()方法。對應的Java代碼是:employee.getAddress().getCountry()
3.執行JavaBean方法
OGNL不僅可以自動執行getter方法,還能執行JavaBean的其它方法,更重要的是,在執行方法時,還能給方法傳入參數。OGNL執行的方法或以靜態方法或非靜態方法。
執行靜態方法的格式爲:@類的全限名@方法名(參數),爲了能讓OGNL執行靜態方法,必須在struts.xml文件中配置常量,以開啓調用靜態方法功能,配置代碼如下:
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
以下示例演示了使用OGNL執行JavaBean方法:
(1) 創建一個JavaBean,命名爲OgnlInfo.java,代碼如下:
public class OgnlInfo {
//定義靜態方法
public static String sayBye(String name)
{
return name+"再見!";
}
//定義非靜態方法
public String sayHello(String name)
{
return name+"你好!";
}
}
(2) 創建index.jsp頁面,代碼如下:
<body>
<%
//實例化ognl對象
OgnlInfo ognlInfo=new OgnlInfo();
//將ognlInfo對象存入request作用域內
request.setAttribute("ognlInfo",ognlInfo);
%>
<!-- 執行ognl對象的非靜態方法 -->
<s:property value="#request.ognlInfo.sayHello('jack')"/>
<!-- 執行ognl對象的靜態方法 -->
<s:property value="@com.soft.struts8l.entity.OgnlInfo@sayBye('tom')"/>
</body>
運行index.jsp頁面,結果如圖1.8.3所示:
圖1.8.3 OGNL執行JavaBean的方法
4.OGNL操作符號
OGNL表達式中能使用的操作符基本跟Java裏的操作符一樣,除了能使用 +, -, *, /, ==, !=, = 等操作符之外,還能使用 mod(取餘), in, not in等
5. %符號
%符號的用途是在標誌的屬性爲字符串類型時,計算OGNL表達式的值。以下示例代碼用於演示%符號在OGNL中的使用方法。
<body>
<%
String title="提交";
pageContext.setAttribute("title",title);
%>
不使用%符號,會按原樣顯示
<s:submit value="#attr.title"/><hr>
使用%符號,會計算OGNL表達式的值
<s:submit value="%{#attr.title}"/>
</body>
運行此示例,結果如圖1.8.4 所示:
圖1.8.4 OGNL %符號的用法
6. OGNL中的集合操作
(1) 生成List類型集合
{e1,e2,e3 . . . }
以上語法,直接生成一個List類型集合,該集合包含了3個元素:e1、e2和e3。如果需要更多元素,則多個元素之間以英文逗號隔開。
(2) 生成Map類型集合
#{key1:value1,key2:value2,. . . }
以上語法,直接生成一個Map類型集合,該Map中每個key-value對象之間以英文冒號隔開;多項之間以英文逗號隔開。
(3) 使用in 和 not in 運算符
對於集合,OGNL提供兩上元素符:in和not in,其中in判斷某個元素是否在指定集合中;not in則用於判斷某個元素是否不在指定集合中。以下代碼演示了in和not in的用法:
<!-- 判斷java是否在指定集合中 -->
<s:if test=" 'java' in {'java','jsp','html'}">
在裏面
<s:else>
不在裏面
</s:else>
</s:if>
(4) 獲取集合的子集
OGNL允許通過某個規則取得集合的子集。取得子集時有以下3種操作符:
?:取出所有符合選擇邏輯的元素
^:取出符合選擇邏輯的第一個元素
$:取出符合選擇邏輯的最後一個元素
假定在session作用域中存在名爲persons集合, 該集合由多個Person類的實例組成, 在Person類中存在一個age屬性,用於描述年齡,則#session.persons.{? #this.age>20} 表達式用於取出年齡大於20的persons類子集合;#session.persons.{? ^this.age>20} 表達式用於取出第一個年齡大於20的Person類實例;#session.persons.{? ^this.age>20} 表達式用於取出最後一個年齡大於20的Person類實例。