OGNL詳解

南夏發表於2015-12-08

struts2三種表單值的傳遞方式及OGNL詳解

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


struts傳值方式 第一種:所有屬性都在action中寫出。


第二種:高階輸入,在action中寫private User user;然後寫上get set方法,並且在JSP頁面中用user.username才能夠獲得。


第三種:http://book.51cto.com/art/200806/77999.htm


Struts 2中的OGNL:http://www.blogjava.net/max/archive/2007/04/28/114417.html(極品好文章)


http://developer.51cto.com/art/201203/322509.htm


http://struts2.group.iteye.com/group/wiki/1356-how-to-use-ognl-in-struts2



Struts 2支援以下幾種表示式語言:
1. OGNL(Object-Graph Navigation Language),可以方便地操作物件屬性的開源表示式語言; 
2. JSTL(JSP Standard Tag Library),JSP 2.0整合的標準的表示式語言; 
3. Groovy,基於Java平臺的動態語言,它具有時下比較流行的動態語言(如Python、Ruby和Smarttalk等)的一些起特性; 
4. Velocity,嚴格來說不是表示式語言,它是一種基於Java的模板匹配引擎,具說其效能要比JSP好。 


Struts 2預設的表示式語言是OGNL,原因是它相對其它表示式語言具有下面幾大優勢:
1. 支援物件方法呼叫,如xxx.doSomeSpecial(); 
2. 支援類靜態的方法呼叫和值訪問,表示式的格式為@[類全名(包括包路徑)]@[方法名 | 值名],例如:@java.lang.String@format('foo %s', 'bar')或@tutorial.MyConstant@APP_NAME; 
3. 支援賦值操作和表示式串聯,如price=100, discount=0.8, calculatePrice(),這個表示式會返回80; 



OGNL是通常要結合Struts 2的標誌一起使用,如<s:property value="xx" />等。大家經常遇到的問題是#、%和$這三個符號的使用。

  1. 訪問OGNL上下文和Action上下文,#相當於ActionContext.getContext();下表有幾個ActionContext中有用的屬性: image
  2. 用於過濾和投影(projecting)集合,如books.{?#this.price<100}。
  3. 構造Map,如#{'foo1':'bar1', 'foo2':'bar2'}。

個人覺得OGNL真的很簡單,即使有的表達方式忘了也沒關係,一查就搞定了,尚學堂的教學中有個例子不錯,基本上常用的都包括了。

下面是OGNL表示式的使用例子。

<?xml version="1.0" encoding="GB18030" ?>
<%@ page language="java" contentType="text/html; charset=GB18030" pageEncoding="GB18030"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=GB18030" />
<title>OGNL表示式語言學習</title>
</head>
<body>
	<ol>
		<li>訪問值棧中的action的普通屬性: username = <s:property value="username"/> </li>
		<li>訪問值棧中物件的普通屬性(get set方法):<s:property value="user.age"/> | <s:property value="user['age']"/> | <s:property value="user[\"age\"]"/> | wrong: <%--<s:property value="user[age]"/>--%></li>
		<li>訪問值棧中物件的普通屬性(get set方法): <s:property value="cat.friend.name"/></li>
		<li>訪問值棧中物件的普通方法:<s:property value="password.length()"/></li>
		<li>訪問值棧中物件的普通方法:<s:property value="cat.miaomiao()" /></li>
		<li>訪問值棧中action的普通方法:<s:property value="m()" /></li>
		<hr />
		<li>訪問靜態方法:<s:property value="@com.bjsxt.struts2.ognl.S@s()"/></li>
		<li>訪問靜態屬性:<s:property value="@com.bjsxt.struts2.ognl.S@STR"/></li>
		<li>訪問Math類的靜態方法:<s:property value="@@max(2,3)" /></li>
		<hr />
		<li>訪問普通類的構造方法:<s:property value="new com.bjsxt.struts2.ognl.User(8)"/></li>
		<hr />
		<li>訪問List:<s:property value="users"/></li>
		<li>訪問List中某個元素:<s:property value="users[1]"/></li>
		<li>訪問List中元素某個屬性的集合:<s:property value="users.{age}"/></li>
		<li>訪問List中元素某個屬性的集合中的特定值:<s:property value="users.{age}[0]"/> | <s:property value="users[0].age"/></li>
		<li>訪問Set:<s:property value="dogs"/></li>
		<li>訪問Set中某個元素:<s:property value="dogs[1]"/></li>
		<li>訪問Map:<s:property value="dogMap"/></li>
		<li>訪問Map中某個元素:<s:property value="dogMap.dog101"/> | <s:property value="dogMap['dog101']"/> | <s:property value="dogMap[\"dog101\"]"/></li>
		<li>訪問Map中所有的key:<s:property value="dogMap.keys"/></li>
		<li>訪問Map中所有的value:<s:property value="dogMap.values"/></li>
		<li>訪問容器的大小:<s:property value="dogMap.size()"/> | <s:property value="users.size"/> </li>
		<hr />
		<li>投影(過濾):<s:property value="users.{?#this.age==1}[0]"/></li>
		<li>投影:<s:property value="users.{^#this.age>1}.{age}"/></li>
		<li>投影:<s:property value="users.{$#this.age>1}.{age}"/></li>
		<li>投影:<s:property value="users.{$#this.age>1}.{age} == null"/></li>
		<hr />
		<li>[]:<s:property value="[0].username"/></li>
	</ol>
	<s:debug></s:debug>
</body>
</html>

下面是對應的java,都是最簡單的class

OgnlAction.java

package com.bjsxt.struts2.ognl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.opensymphony.xwork2.ActionSupport;

public class OgnlAction extends ActionSupport {
	private Cat cat;
	private Map<String, Dog> dogMap = new HashMap<String, Dog>();
	private Set<Dog> dogs = new HashSet<Dog>();

	private String username;
    private String password;

    private User user;
    private List<User> users = new ArrayList<User>();

	public OgnlAction() {
		users.add(new User(1));
		users.add(new User(2));
		users.add(new User(3));

		dogs.add(new Dog("dog1"));
		dogs.add(new Dog("dog2"));
		dogs.add(new Dog("dog3"));

		dogMap.put("dog100", new Dog("dog100"));
		dogMap.put("dog101", new Dog("dog101"));
		dogMap.put("dog102", new Dog("dog102"));
	}

	public String execute() {
		return SUCCESS;
	}

    public String m() {
        return "hello";
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    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;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Map<String, Dog> getDogMap() {
        return dogMap;
    }

    public void setDogMap(Map<String, Dog> dogMap) {
        this.dogMap = dogMap;
    }

    public Set<Dog> getDogs() {
        return dogs;
    }

    public void setDogs(Set<Dog> dogs) {
        this.dogs = dogs;
    }

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

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

Dog.java

package com.bjsxt.struts2.ognl;

public class Dog {
	
	private String name;
	
	public Dog() {
		
	}

	public Dog(String name) {
        super();
        this.name = name;
	}
	
	public String getName() {
		return name;
	}

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

	@Override
	public String toString() {
		return "dog: " + name;
	}
}

 

Cat.java

package com.bjsxt.struts2.ognl;

public class Cat {
	
	private Dog friend;
	
	public Dog getFriend() {
		return friend;
	}

	public void setFriend(Dog friend) {
		this.friend = friend;
	}

	public String miaomiao() {
		return "miaomiao";
	}
}

User.java

package com.bjsxt.struts2.ognl;

public class User {
	private int age = 8;
	
	public User() {
		
	}
	
	public User(int age) {
		super();
		this.age = age;
	}


	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	public String toString() {
		return "user" + age;
	}
}

S.java

package com.bjsxt.struts2.ognl;

public class S {
	public static String STR = "STATIC STRING";
	
	public static String s() {
		return "static method";
	}
}



平時使用Struts2標籤時會出現一些很奇特的問題,對於OGNL不瞭解的人可能對問題的出現無能為力或者就算解決了問題也不知道是如何解決的。下面總結一些使用Struts2標籤容易出現的困惑:

問題一:#,%{},$符號

在Struts2標籤屬性中經常會出現"#"或者"%{}"的符號出現,通過上面OGNL表示式基礎的介紹,知道了OGNL上下文中有且僅有一個根物件。Struts2為我們定義了許多明明物件,他們分別是"ValueStack","Parameters","Session","Request", "Appliction","Attr",其中"ValueStack"被設定為上下文的根物件。訪問非根物件必須加上"#"號,這就是出現"#"的原因。Struts2中的標的處理類,並不是所有都將標籤的屬性作為OGNL表示式來看待,有時候我們需要設定動態地值,則必須告訴標籤的處理類該字串按照OGNL表示式來處理,%{}符號的作用就是告訴標籤的處理類將它包含的字串按照OGNL表示式處理。 "$"符號用於XML檔案中用於獲取動態值,與%{}作用類似。

問題二:%{}符號的影響

Struts2的標籤幾十幾百個,要記住哪一個標籤的處理類將標籤的屬性作為OGNL表示式是一件很困難的事情,在不清楚處理類的處理方式時怎麼辦,%{}對於標籤處理類來說,若處理類將屬性值作為普通字串則%{}符號包含的字串當做OGNL表示式,若處理類將屬性值作為OGNL表示式來處理,則直接忽略%{}符號。換句話說,不清楚處理方式的話,可以都使用%{}符號。

問題三:標籤是如何獲得資料

下面是ValueStack的官方描述:

ValueStack allows multiple beans to be pushed in and dynamic EL expressions to be evaluated against it. When evaluating an expression, the stack will be searched down the stack, from the latest objects pushed in to the earliest, looking for a bean with a getter or setter for the given property or a method of the given name (depending on the expression being evaluated).

大致意思:ValueStack允許儲存多個bean(也就是Action),並且可以使用表示式語言獲得他們。當評估一個表示式,ValueStack將會從棧頂到棧底的方向被搜尋一遍,對於給定的屬性名稱尋找bean的getter或setter方法或尋找給定的方法。

每當一個請求到達Action時,Struts2會將Action物件推入ValueStack中。

  1. <body>   
  2.     username:<s:property value="username"/><br />  
  3.     -------------------詭異的分割線-------------------<br />  
  4.     username:<%= ((HelloWorldAction)ActionContext.getContext().getValueStack().peek()).getUsername() %><br />  
  5.   </body> 

頁面顯示結果:

  1. username:zhangsan  
  2. -------------------詭異的分割線-------------------  
  3. username:zhangsan 

可以看到標籤取值與用Java程式碼取值的結果相同,明顯標籤的取值方式更簡練簡潔。OGNL表示式"username"表示了從根物件ValueStack中取出屬性username的值。它會從棧頂到棧底遍歷ValueStack,直到找某一個Action中的"username"屬性。