1. 程式人生 > >OGNL表示式和Struts2標籤

OGNL表示式和Struts2標籤

OGNL的全稱是Object Graph Navigation Language(物件圖形導航語言),它是一種強大的表示式語言,開發者可以通過簡單一致的表示式語法來讀取和設定java物件的屬性值,呼叫物件的方法,遍歷整個物件的結構。

操作物件!

OGNL有一個上下文(Context)概念,通俗的說就是一個Map結構,它實現了java.util.Map介面,在struts2中上下文(Context)的實現為ActionContext。即Struts2中OGNL Context實現者為ActionContext,它的結構示意圖如下:


當Struts2接受一個請求時,會迅速建立ActionContext,然後建立ValueStack,再建立action。接著把action存放到ValueStack中,所以action中的例項變數可以被OGNL表示式直接訪問。

OGNL表示式訪問各個名稱空間的屬性:

1.訪問上下文(Context)中的物件需要使用#符號標註名稱空間,如#application、#Session...

2.另外OGNL會設定一個根物件(root物件),在struts2中根物件就是ValueStack(值棧)。如果要訪問根物件(即值棧ValueStack)中物件的屬性,則可以省略#名稱空間,直接訪問該物件的屬性即可。

3.在struts2中,根物件ValueStack的實現類為OgnlValueStack,該物件不是存放單個值,而是存放一組物件。在OgnlValueStack類裡面有一個List型別的root變數,就是使用它來存放一組物件

,如圖:


4.在root變數中處於第一位的物件叫做棧頂物件。通常我們在OGNL表示式裡直接寫上熟悉你的名稱即可訪問root變數裡物件的屬性,搜尋順序是從棧頂物件開始尋找,如果棧頂物件不存在該屬性,就會從第二個物件開始尋找,依次往下訪問,知道找到為止。

如:要訪問存在棧頂中的一個name屬性,我們不需要用到#名稱空間的方式,可以直接使用物件屬性名稱的方式訪問,即寫個name即可,但是這樣寫又是無效的,在struts2中,OGNL表示式需要配合struts2標籤才可以使用,應該寫成這樣:<s:property value="name">,這個name就是OGNL表示式。

5.訪問值棧ValueStack中的物件,除了直接寫屬性名稱以外,還可以使用EL表示式直接訪問,如上面的name,可以這樣訪問:${name}。

注:EL表示式只限於訪問值棧ValueStack中的物件的屬性,如果要訪問其他域的屬性還得使用#名稱空間的方式。

Application物件:用於訪問ServletContext,例如:#application.userName或者#application['userName'](注【】的形式在一些特殊字元的時候使用,如#applicaiton['u-name']),這樣相當於呼叫了ServletContext的getAttribute("username");

session物件:用來訪問HttpSession,例如:#sesion.userName或者#session['userName'],相當於呼叫session.getAttribute("userName");

request物件:用來訪問HttpServletRequest屬性,例如:#request.userName或者#request['userName'],相當於呼叫request.getAttribute("userName");

parameters物件:用來訪問Http的請求引數,例如:#parameters.userName或者#parameters['userName'],相當於呼叫request.getParameter("userName");

attr物件:用於按page->request->session->application順序訪問屬性。

在JSP中使用strus2的標籤,必須要引入標籤宣告:

<%@taglib prefix="s" uri="/struts-tags" %>

1.set標籤:用於將某個值放入指定的範圍。

引數:


注:如果沒有設定scope範圍,那麼預設為OGNL Context(訪問時不需要名稱空間,使用#即可)。

測試程式碼:

<body>
   		<s:set name="age" value="22"></s:set>
  </body>

注:上述程式碼沒有指定範圍,即將age放入OGNL Context中,訪問時使用#age即可。

2.property標籤:用於輸出指定的值:

value屬性:可選,指定需要輸出的屬性值,如果沒有該屬性,則預設輸出ValueStack棧頂的值(這個非常重要,有些時候不寫value也可以輸出,是因為要訪問的屬性在棧頂)。

測試程式碼1(訪問request範圍的屬性):

<body>
	<%
		request.setAttribute("name", "習近平");
	%> 
	
	<!-- 訪問request範圍內的屬性,要使用#名稱空間的形式 -->
	名稱:<s:property value="#request.name"/> 		
  </body>

效果:


測試程式碼2(訪問Session範圍的屬性):

 <body>
	<%
		session.setAttribute("name", "李克強");
	%> 
	
	<!-- 訪問session範圍內的屬性,要使用#名稱空間的形式 -->
	名稱:<s:property value="#session.name"/> 		
  </body>

效果:


測試程式碼3(訪問值棧中的屬性):

/**
 * 在請求訪問action的時候,系統會迅速建立ActionContext->valueStack->action
 * 並且把action存放在valueStack中,這就是為什麼OGNL表示式可以直接以屬性名稱的方式訪問屬性。
 * 因為action存放在了valueStack中。
 * @author Liao
 *
 */
public class UserAction extends ActionSupport {

	private static final long serialVersionUID = 1L;

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public String execute(){
		
		this.setName("胡錦濤");
		
		return this.SUCCESS;
	}
}

在介面訪問:
 <body>
	<!-- 訪問valueStack中的屬性,不需要使用#名稱空間的形式,直接通過屬性名的方式即可訪問 -->
	名稱:<s:property value="name"/> 	<br>
	<!-- 值棧的值也可以直接通過EL表示式來訪問 -->
	通過EL表示式獲取的名稱:${name}	
  </body>


效果:


3.Iterator迭代標籤:用於遍歷集合(java.util.Collection)或者列舉值(java.util.iterator)。

引數:


注:value為可選屬性,指定被迭代的集合,如果沒有設定該屬性,則使用ValueStack棧頂的集合。

在演示之前我們先看另外一個知識點:通過OGNL表示式來建立List/Map集合。

測試程式碼:

<body>
  	<!-- 通過OGNL可以建立List和Map集合value="{'鄧小平','胡錦濤','習近平'}"表示一個List集合 -->
  	<s:set  name="list" value="{'鄧小平','胡錦濤','習近平'}"/>
  	
  	<!-- 上述集合沒有指定範圍就表示OGNL Context範圍內的變數,使用#即可 ,iterator標籤中的value表示要迭代的集合-->
  	<s:iterator value="#list">
  	<!-- 注:Iterator標籤在迭代集合時,會把當前迭代的物件放在值棧的棧頂,所以使用property標籤輸出時可以不用value -->
  		<s:property /><br>
  	</s:iterator>
  </body>

注:一定要記住,iterator標籤在迭代集合時會把當前迭代的物件放在值棧的棧頂,這就是為什麼使用property輸出值時可以不寫value屬性的原因。

 效果:


使用OGNL建立Map集合:

測試程式碼【通過OGNL可以建立List和Map集合value="#{'key1':'鄧小平','key2':'胡錦濤','key3':'習近平'}"表示一個map集合(注意書寫方式)】:

測試程式碼:

<body>

	<s:set name="maps" value="#{'key1':'鄧小平','key2':'胡錦濤','key3':'習近平'}" />

	<!-- 上述集合沒有指定範圍就表示OGNL Context範圍內的變數,使用#即可 ,iterator標籤中的value表示要迭代的集合-->
	<s:iterator value="#maps">
		<!-- 注:Iterator標籤在迭代集合時,會把當前迭代的物件放在值棧的棧頂,所以使用property標籤輸出時可以不用value -->
		<s:property value="key" />=<s:property value="value" />
		</br>
	</s:iterator>
</body>

效果:


OGNL表示式的投影功能,常用操作符:

1.?表示獲得所有符合邏輯的元素。

2.^表示符合邏輯的第一個元素。

3.$表示符合邏輯的最後一個元素。

測試案例:

User物件:

public class User {

	private Integer uid;
	private String uname;
	private Integer age;

	public User() {
	}

	public User(Integer uid, String uname, Integer age) {
		this.uid = uid;
		this.uname = uname;
		this.age = age;
	}

	public Integer getAge() {
		return age;
	}

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

	public Integer getUid() {
		return uid;
	}

	public void setUid(Integer uid) {
		this.uid = uid;
	}

	public String getUname() {
		return uname;
	}

	public void setUname(String uname) {
		this.uname = uname;
	}

}

UserAction.java:
/**
 * 在請求訪問action的時候,系統會迅速建立ActionContext->valueStack->action
 * 並且把action存放在valueStack中,這就是為什麼OGNL表示式可以直接以屬性名稱的方式訪問屬性。
 * 因為action存放在了valueStack中。
 * 這個類中的users屬性可以直接通過屬性名稱的方式訪問
 * @author Liao
 *
 */
public class UserAction extends ActionSupport {

	private static final long serialVersionUID = 1L;

	private List<User> users;
	
	public List<User> getUsers() {
		return users;
	}

	public void setUsers(List<User> users) {
		this.users = users;
	}

	public String execute(){
		
		users = new ArrayList<User>();
		users.add(new User(1, "習近平", 20));
		users.add(new User(1, "胡錦濤", 30));
		users.add(new User(1, "鄧小平", 40));
		
		return this.SUCCESS;
	}
}

前臺投影測試:
<body>

	<!-- users是action中的屬性,即存放在ValueStack中,所以訪問users不需要使用#名稱空間的形式 -->
	<s:iterator value="users.{?#this.age > 30}">
		<s:property value="uname" />:<s:property value="age" />
		</br>
	</s:iterator>
</body>

注:users是action中的屬性,當然也是ValueStack中的物件,所以可以用屬性名稱的方式直接訪問。在上述程式碼中,直接在集合後緊跟.{}運算子表明用於取出該集合的子集,{}的表示式用於獲取符合條件的元素,this指的是為了從大集合users帥選資料到小集合,需要對大集合users進行迭代,this代表當前迭代的元素。

效果:


iterator標籤中的status屬性用來指定迭代時的IteratorStatus例項,該方法包含如下集合方法:

int getCount(),返回當前迭代了幾個元素。
int getIndex(),返回當前迭代元素的索引。
boolean isEven(),返回當前被迭代元素的索引是否是偶數
boolean isOdd(),返回當前被迭代元素的索引是否是奇數
boolean isFirst(),返回當前被迭代元素是否是第一個元素。
boolean isLast(),返回當前被迭代元素是否是最後一個元素。

有了這些方法,我麼可以做一些特殊效果:

測試程式碼:

 <body>
	<s:set name="list" value="{'鄧小平','胡錦濤','習近平'}" />
		
	<s:iterator value="#list" status="s">
	<!-- 如果是奇數就為紅色,否則為藍色 -->
	<font color=<s:if test="#s.odd">red</s:if>
				<s:else>blue</s:else>>
	<s:property/>
	</font>
	
	</s:iterator>  		
  </body>

效果:


4.if elseif else 標籤:執行基本的條件流轉。

引數:

測試代1:

<body>
  
   		<s:set name="age" value="22"></s:set>
   		
   		<!-- 使用ognl接收值 -->
   		<s:if test="#age > 20">
   			您的青春已經讓狗給吃了!
   		</s:if>
   		
   		<s:elseif test="#age > 35">
   			七年之癢,勿出軌!
   		</s:elseif>
   			
   		<s:else>
   			青春年華,無限風光!
   		</s:else>
   		
  </body>

效果:

OGNL表示式可以使用in和not in兩個元素符號,in表示式用來判斷某個元素是否在指定的集合物件中,not in判斷某個元素是否不在某個集合物件中。

測試程式碼2:

 <body>
   		<!-- OGNL表示式in的測試 -->
   		<s:if test="'liao' in {'liao','zhong','min'}">
   			您中大獎啦!
   		</s:if>
   		<s:else>
   			您被坑啦!
   		</s:else>
   		
   		<!-- OGNL表示式not in的測試 -->
   		<s:if test="'liao' not in {'lavimer','hello'}">
   			你不在啊!
   		</s:if>
   		<s:else>
   			你在啊!
   		</s:else>
  </body>


注:<s:select>下拉框、<s:checkboxlist>複選框、<s:radio>單選框在Web開發中經常使用,將在後面的文章中以專題的形式詳細討論。