1. 程式人生 > >OGNL表示式語言詳解

OGNL表示式語言詳解

眾所周知,OGNL充斥在Struts2前後臺數據傳遞與儲存的方方面面,給Struts2中資料的處理帶來了極大的方便,所以,本次打算以兩篇部落格來介紹OGNL,第一篇就是脫離MVC框架,單純介紹OGNL的一般用法和語法,在第二篇部落格裡面,介紹OGNL的實際應用,本篇是第一篇,以介紹OGNL的語法和一般用法為主;

OGNL(Object-Graph Navigation Language的簡稱),物件圖導航語言,它是一門表示式語言,除了用來設定和獲取Java物件的屬性之外,另外提供諸如集合的投影和過濾以及lambda表示式等,那麼我們先來開啟第一個例子,首先新建一個Java Project,由於OGNL是Apache開源專案的子專案,所以需要額外匯入兩個jar,分別為:ognl-x.x.xx.jar和javassist-x.x.xx.jar,搭建完畢後項目結構如下:


接下來給大家展示兩個JavaBean類,整個OGNL的介紹,將圍繞著這兩個類展開

package com.ognl.domain;

import java.util.HashSet;
import java.util.Set;

public class SlDept implements java.io.Serializable {

	private static final long serialVersionUID = 3537237434024057830L;
	
	private String name;
	
	private Set<SlEmployee> slEmployees = new HashSet<SlEmployee>(0);

	public SlDept() {
	
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Set<SlEmployee> getSlEmployees() {
		return slEmployees;
	}

	public void setSlEmployees(Set<SlEmployee> slEmployees) {
		this.slEmployees = slEmployees;
	}

}
package com.ognl.domain;

public class SlEmployee implements java.io.Serializable {

	private static final long serialVersionUID = 4873217019660076767L;

	private SlDept slDept;
	
	private String name;

	public SlEmployee() {
	}

	public SlDept getSlDept() {
		return slDept;
	}

	public void setSlDept(SlDept slDept) {
		this.slDept = slDept;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}


下面編寫一個類,程式碼如下:

package com.ognl.test;

import com.ognl.domain.SlDept;
import com.ognl.domain.SlEmployee;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

public class OgnlTest {
	
	public static void main(String[] args) throws OgnlException {
		// 新建一個部門物件並設定部門名稱
		SlDept dept = new SlDept();
		dept.setName("銷售部");
		
		// 新建一個員工物件並設定員工姓名
		SlEmployee emp = new SlEmployee();
		emp.setName("張三");
		
		// 構建一個OgnlContext物件
		OgnlContext context = new OgnlContext();
		
		// 將上述部門和員工物件放入Ognl上下文環境中
		context.put("dept", dept);
		context.put("emp", emp);
		
		// 將員工設定為根物件
		context.setRoot(emp);
		
		// 構建Ognl表示式的樹狀表示,用來獲取
		Object expression = Ognl.parseExpression("#dept.name");
		
		// 解析樹狀表示式,返回結果
		Object deptName = Ognl.getValue(expression, context, context.getRoot());
		
		System.out.println(deptName);
	}
	
}


最終輸出結果為銷售部,需要注意的是看到上述的"#"是不是特別眼熟,對在Struts2中特別常見,而且大家應該發現所謂的物件圖導航語言指的就是通過 "放置到OgnlContext中的名字.屬性名字" 的方式去獲取對應物件的屬性值;

首先介紹一下Ognl中,常用到的兩個類:

ognl.Ognl類:這個類主要用來解析和解釋執行Ognl表示式

ognl.OgnlContext類:這個類為Ognl表示式提供了一個執行環境,這個類實現了Map介面,所以允許通過put(key,obj)方法向OgnlContext環境中方式各種型別的物件,需要注意的是在OgnlContext中物件分為兩種,第一種是叫做root物件(根物件),在整個OgnlContext中有且最多隻能有一個根物件,可以通過呼叫OgnlContext.setRoot(obj)設定為根物件,另外一種就是OgnlContext中的普通物件,這種個數型別不受限制,那麼既然分為兩種方式,肯定在獲取物件屬性的方式上是有所不同的,下面通過程式碼比較下:

1、獲取普通物件的屬性值方式;

比如上述例子當中,dept物件就是放置到OgnlContext中的普通物件,對於這種普通物件,只能通過“#dept.name”的方式去獲取屬性值,需要注意的是dept指的是放置到上下文中key的值,另外在Dept型別中要提供getName方法;

2、獲取根物件的屬性值的方式,有兩種,第一種也是跟上述方式一樣,不多做敘述

Object expression = Ognl.parseExpression("#emp.name");
第二種,直接寫屬性名稱就可以,注意:這個時候就不要加“#”,如下
expression = Ognl.parseExpression("name");
原理是這樣的,如果Ognl在解析表示式的時候發現表示式開頭帶有"#",會去普通物件中,去尋找,如果沒有"#",則會預設去根物件中去尋找,由於根物件只有一個,所以只需要屬性名字

綜合上述兩種方式,通過一個直觀的例子,理解一下:

package com.ognl.test;

import com.ognl.domain.SlDept;
import com.ognl.domain.SlEmployee;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

public class OgnlTest {

	public static void main(String[] args) throws OgnlException {
		// 新建一個部門物件並設定部門名稱
		SlDept dept = new SlDept();
		dept.setName("軟體開發部");

		// 新建一個員工物件並設定員工姓名
		SlEmployee emp = new SlEmployee();
		emp.setName("李四");
		emp.setSlDept(dept);

		// 構建一個OgnlContext物件
		OgnlContext context = new OgnlContext();

		// 將上述部門和員工物件放入Ognl上下文環境中
		context.put("dept", dept);
		context.put("emp", emp);

		// 將員工設定為跟物件
		context.setRoot(emp);

		// 從普通物件中直接獲取部門名稱
		Object expression = Ognl.parseExpression("#dept.name");
		Object deptName = Ognl.getValue(expression, context, context.getRoot());
		System.out.println(deptName);
		System.out.println("-------------------------------------------");
		// 間接獲取部門名稱
		expression = Ognl.parseExpression("#emp.slDept.name");
		deptName = Ognl.getValue(expression, context, context.getRoot());
		System.out.println(deptName);
		System.out.println("-------------------------------------------");
		// 從根物件中直接獲取部門名稱
		expression = Ognl.parseExpression("slDept.name");
		deptName = Ognl.getValue(expression, context, context.getRoot());
		System.out.println(deptName);
	}

}

通過例子可以發現,咱們主要工作就是用來學習如何讓表示式合法,能夠被正確解析且解釋執行就可以了;

下面,相信通過上面的例子,我們對Ognl表示式有了一個認識,下面開始學習如何在Ognl表示式用呼叫方法,大家都知道方法分為靜態方法和非靜態方法,好的,看例子:

package com.ognl.test;

import com.ognl.domain.SlDept;
import com.ognl.domain.SlEmployee;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

public class OgnlTest2 {

	public static void main(String[] args) throws OgnlException {
		// 新建一個部門物件並設定部門名稱
		SlDept dept = new SlDept();
		dept.setName("上海-軟體技術支援部");

		// 新建一個員工物件並設定員工姓名
		SlEmployee emp = new SlEmployee();
		emp.setName("李小龍");
		emp.setSlDept(dept);
		emp.setAge("22");
		// 構建一個OgnlContext物件
		OgnlContext context = new OgnlContext();

		// 將上述部門和員工物件放入Ognl上下文環境中
		context.put("dept", dept);
		context.put("emp", emp);

		// 將員工設定為跟物件
		context.setRoot(emp);

		// 從根物件中直接獲取部門名稱長度,非靜態方法
		Object expression = Ognl.parseExpression("slDept.name.length()");
		Object length = Ognl.getValue(expression, context, context.getRoot());
		System.out.println(length);
		
		// 在Ognl表示式中使用靜態方法
		expression = Ognl.parseExpression("@[email protected](age)");
		length = Ognl.getValue(expression, context, context.getRoot());
		System.out.println(length);
		
	}

}
第三、接下來介紹,Ognl表示式中如何處理陣列、集合和Map物件
package com.ognl.test;

import java.util.ArrayList;
import java.util.List;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

public class OgnlTest3 {

	public static void main(String[] args) throws OgnlException {

		OgnlContext context = new OgnlContext();

		// 通過Ognl表示式構建一個LinkedList物件,這注意:一定是包名+類名的形式
		Object list = Ognl.parseExpression("new java.util.LinkedList()");
		Object obj = Ognl.getValue(list, context, context.getRoot());
		System.out.println(obj);
		System.out.println("----------------------------");

		// 在Ognl中提供了一種類似陣列索引的方式訪問集合指定位置的元素
		// 下述例子直接構建了一個包含aa, bb, cc, dd四個元素的集合,然後訪問集合中的第三個元素
		Object object15 = Ognl.getValue("{'aa', 'bb', 'cc', 'dd'}[2]", context, context.getRoot());
		System.out.println(object15);
		System.out.println("----------------------------");
		
		// 處理陣列型別
		String[] strs = new String[] { "aa", "bb", "cc" };
		context.put("strs", strs);
		System.out.println(Ognl.getValue("#strs[1]", context, context.getRoot()));
		System.out.println("----------------------------");
		
		// 處理集合型別
		List<String> words = new ArrayList<String>();
		words.add("hello");
		words.add("world");
		words.add("hello world");
		context.put("words", words);
		System.out.println(Ognl.getValue("#words[0]", context, context.getRoot()));
		System.out.println("----------------------------");
		
		// 處理Map型別,注意的是為了與集合區分開,在大括號前面加"#"
		System.out.println(
				Ognl.getValue("#{'key1': 'value1', 'key2': 'value2', 'key3': 'value3', 'key4': 'value4'}['key3']",
						context, context.getRoot()));

	}
}
第四、下面介紹Ognl中的過濾和投影兩個概念,簡單介紹下,無論過濾還是投影都是針對於陣列、集合和Map而言的;

過濾指的是將原集合中不符合條件的物件過濾掉,然後將滿足條件的物件,構建一個新的集合物件返回,Ognl過濾表示式的寫法是:collection.{?|^|$   expression};

投影指的是將原集合中所有物件的某個屬性抽取出來,單獨構成一個新的集合物件返回,基礎語法為 :collection.{expression};

示例程式碼如下:

package com.ognl.domain;

public class Student {
	
	private String name;
	
	private int age;
	
	private double height;

	public Student() {
		super();
	}

	public Student(String name, int age, double height) {
		super();
		this.name = name;
		this.age = age;
		this.height = height;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getHeight() {
		return height;
	}

	public void setHeight(double height) {
		this.height = height;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", height=" + height + "]";
	}
}
package com.ognl.test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.ognl.domain.Student;

import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;

public class OgnlTest4 {

	public static void main(String[] args) throws OgnlException {

		Student s1 = new Student("Tom", 22, 170.3);
		Student s2 = new Student("Jack", 21, 176.2);
		Student s3 = new Student("Tomas", 23, 180.1);
		Student s4 = new Student("Lucy", 20, 163.3);

		List<Student> stus = new ArrayList<Student>();
		Collections.addAll(stus, s1, s2, s3, s4);
		// 新建OgnlContext物件
		OgnlContext context = new OgnlContext();
		context.put("stus", stus);

		// 過濾(filtering),collection.{? expression}
		// 利用過濾獲取身高在175以上的所有學生集合
		// 輸出結果:[Student [name=Jack, age=21, height=176.2], Student [name=Tomas, age=23, height=180.1]]
		System.out.println(Ognl.getValue("#stus.{? #this.height > 175.0}", context, context.getRoot()));

		// 過濾(filtering),collection.{^ expression}
		// 利用過濾獲取身高在175以上的所有學生集合中第一個元素
		// 輸出結果:[Student [name=Jack, age=21, height=176.2]]
		System.out.println(Ognl.getValue("#stus.{^ #this.height > 175.0}", context, context.getRoot()));

		// 過濾(filtering),collection.{$ expression}
		// 利用過濾獲取身高在175以上的所有學生集合的最後一個元素
		// 輸出結果:[Student [name=Tomas, age=23, height=180.1]]
		System.out.println(Ognl.getValue("#stus.{$ #this.height > 175.0}", context, context.getRoot()));
		
		// 投影(projection), collection. {expression}
		// 獲取集合中的所有學生的姓名
		// 輸出結果:[Tom, Jack, Tomas, Lucy]
		System.out.println(Ognl.getValue("#stus.{name}", context, context.getRoot()));
	}

}
本篇部落格從Ognl中對java物件屬性值的訪問,靜態和非靜態方法的呼叫,Ognl如何處理陣列、集合和Map型別的資料以及集合的投影、過濾等幾個方面介紹了日常用到的OGNL知識,這是基礎,但卻又是最關鍵的東西,牢記!