OGNL 相較其他表達式語言具備五大優勢:
xxx.doSomeSpecial()@java.lang.String@format('foo %s', 'bar')price=100, discount=0.8, calculatePrice()public class OGNL1 {
public static void main(String[] args) {
Map<String, Object> context = new HashMap<>();
Person p1 = new Person("zhangsan");
Person p2 = new Person("lisi");
Person p3 = new Person("wangwu");
Person p4 = new Person("zhaoliu"); // 不放入 context
context.put("person1", p1);
context.put("person2", p2);
context.put("person3", p3);
// 1. 從根物件取 name
Ognl.getValue("name", context, p2); // lisi
// 2. 用 # 指定物件取 name
Ognl.getValue("#person2.name", context, p2); // lisi
Ognl.getValue("#person1.name", context, p2); // zhangsan
// 3. 指定 p4 為根物件(p4 不在 context 中)
Ognl.getValue("name", context, p4); // zhaoliu
Ognl.getValue("#person4.name", context, p4); // 拋出異常:person4 不在 context
}
}
關鍵結論:無 # 時從根物件取值;有 # 時從 context 指定鍵取值,且該鍵必須存在於 context 中。
public class OGNL2 {
public static void main(String[] args) {
OgnlContext context = new OgnlContext();
People p1 = new People("zhangsan");
context.setRoot(p1);
// 成員方法
Ognl.getValue("name.length()", context, context.getRoot()); // 8
Ognl.getValue("#people2.name.toUpperCase()", context, context.getRoot()); // LISI
// 靜態方法
Ognl.getValue("@java.lang.Math@min(4,10)", context, context.getRoot()); // 4
// 靜態欄位
Ognl.getValue("@java.lang.Math@E", context, context.getRoot()); // 2.718...
}
}
語法重點:@完整類名@方法名(參數) 呼叫靜態方法,@完整類名@欄位名 存取靜態欄位。
public class OGNL3 {
public static void main(String[] args) {
OgnlContext context = new OgnlContext();
Classroom cr = new Classroom();
cr.getStudents().addAll(Arrays.asList("zhangsan", "lisi", "wangwu"));
context.setRoot(cr);
// 存取集合
Ognl.getValue("students", context, context.getRoot()); // [zhangsan, lisi, wangwu]
Ognl.getValue("students[0]", context, context.getRoot()); // zhangsan
Ognl.getValue("students.size()", context, context.getRoot()); // 3
// Map 存取
Student stu = new Student();
stu.getContactWays().put("homeNumber", "110");
context.put("student", stu);
Ognl.getValue("#student.contactWays['homeNumber']", context, context.getRoot()); // 110
// 建構集合
Ognl.getValue("{'aa','bb','cc'}", context, context.getRoot()); // List
Ognl.getValue("#{'key1':'value1','key2':'value2'}", context, context.getRoot()); // Map
}
}
OGNL 視 List 與 Array 同等,建構 List 用 {},建構 Map 用 #{}。
public class OGNL4 {
public static void main(String[] args) {
Humen h = new Humen("qiuyi", "n", 22);
h.getFriends().addAll(Arrays.asList(
new Humen("zhangsan", "n", 22),
new Humen("lisi", "f", 21),
new Humen("wangwu", "n", 23)
));
OgnlContext context = new OgnlContext();
context.setRoot(h);
// 過濾:名字長度 > 7
Ognl.getValue("friends.{? #this.name.length() > 7}", context, context.getRoot());
// 結果: [Humen[name=zhangsan, ...]]
// 投影:取所有名字
Ognl.getValue("friends.{name}", context, context.getRoot());
// 結果: [zhangsan, lisi, wangwu]
}
}
collection.{? expression},#this 代表當前元素collection.{expression},類似 SQL SELECT column#、%、$ 符號Struts 2 將 ValueStack 設為根物件,ValueStack、Parameters、Session、Request、Application、Attr 均在 Context 中。存取非根物件加 #;% 強制字串屬性按 OGNL 求值;$ 用於 XML/國際化檔引用。
% 的通用性不確定標籤處理類是否將屬性視為 OGNL 時,統一加 % 最安全:若處理類視為字串則求值,若視為 OGNL 則忽略 %。
ValueStack 從棧頂向下搜尋,找到含目標屬性 getter/setter 的 Bean。<s:property value="username"/> 相當於遍歷棧中 Action 找 getUsername()。
<s:property value="user.username"/>
<s:property value="user.address.addr"/>
<s:property value="user.get()"/>
<s:property value="@struts.action.LoginAction@get()"/>
<s:property value="@java.lang.Math@floor(44.56)"/>
<s:property value="@java.util.Calendar@getInstance()"/>
<s:property value="@struts.vo.Address@TIPS"/>
<s:property value="new struts.vo.Student('李曉紅','美女',3,25).username"/>
<s:property value="testList"/> <!-- 整個 List -->
<s:property value="testList[0]"/> <!-- 索引存取 -->
<s:property value="testMap['m1']"/> <!-- Map key 存取 -->
<s:property value="testMap.keys"/> <!-- 所有鍵 -->
<s:property value="testMap.values"/> <!-- 所有值 -->
<s:property value="testSet.size"/> <!-- 偽屬性 size -->
<s:property value="stus.{? #this.grade >= 60}"/> <!-- 過濾 -->
<s:property value="stus.{? #this.grade >= 60}.{username}"/> <!-- 過濾後投影 -->
<s:property value="stus.{^ #this.grade >= 60}.{username}"/> <!-- 第一個符合 -->
<s:property value="stus.{$ #this.grade >= 60}.{username}"/> <!-- 最後一個符合 -->
<s:property value="#f = :[#this==1?1:#this*#f(#this-1)], #f(4)"/> <!-- 4! = 24 -->
<s:property value="#parameters.username"/> <!-- Request 參數 -->
<s:property value="#session.userId"/> <!-- Session -->
<s:property value="#application.config"/> <!-- Application -->
% 與 $ 實戰<s:property value="%{getText('key')}"/> <!-- 取國際化訊息 -->
<s:property value="${min}"/> <!-- 國際化檔/驗證配置 -->
OGNL 是 Struts 2 資料綁定的靈魂。精通上下文機制、掌握三大符號用法、熟練集合操作與投影過濾語法,能讓視圖層程式碼極度精簡。實戰建議:多寫測試案例驗證各種存取路徑,善用 % 避免標籤屬性解析歧義,靈活運用 Lambda 與集合操作處理複雜資料轉換邏輯。