1. 數據傳輸背後機制:ValueStack(值棧)
html
在這一切的背後,是由於有了ValueStack(值棧)! java
ValueStack基礎:OGNL
要了解ValueStack,必須先理解OGNL(Object Graphic Navigatino Language)! 數組
OGNL是Struts2中使用的一種表達式語言,它能夠用於JSP的標籤庫中,以便可以方便的訪問各類對象的屬性;它用於界面將參數傳遞到Action(並進行類型轉換)中;它還能夠用於struts2的配置文件中!因此,很是有必要理解OGNL的基本機制。 ide
Root對象
OGNL稱爲對象圖導航語言。所謂對象圖,即以任意一個對象爲根,經過OGNL能夠訪問與這個對象關聯的其它對象。如: ui
package cn.com.leadfar.struts2.actions; this
public class User { .net
private String username; htm
private Group group; 對象
public String getUsername() { 繼承
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
}
package cn.com.leadfar.struts2.actions;
public class Group {
private String name;
private Organization org;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Organization getOrg() {
return org;
}
public void setOrg(Organization org) {
this.org = org;
}
}
package cn.com.leadfar.struts2.actions;
public class Organization {
private String orgId;
public String getOrgId() {
return orgId;
}
public void setOrgId(String orgId) {
this.orgId = orgId;
}
}
上面三個類,描述了經過一個User對象,能夠導航到Group對象,進而導航到Organization對象,以User對象爲根,一個對象圖以下所示:
User(root)
-- username
-- group
-- name
-- org
-- orgId
在真實的環境下,這個對象圖可能會極其複雜,可是經過基本的getters方法,都應該可以訪問到某個對象的其它關聯對象。【對象圖的導航,必須經過getters方法進行導航】
下述代碼將建立一個User對象,及其相關的一系列對象:
User user = new User();
Group g = new Group();
Organization o = new Organization();
o.setOrgId("ORGID");
g.setOrg(o);
user.setGroup(g);
若是經過JAVA代碼來進行導航(依賴於getters方法),導航到Organization的orgId屬性,以下所示:
//用JAVA來導航訪問
user.getGroup().getOrg().getOrgId();
【注意:導航的目的,是爲了獲取某個對象的值或設置某個對象的值或調用某個對象的方法!】
【注意:OGNL表達式語言的真正目的,是爲了在那些不能寫JAVA代碼的地方執行JAVA代碼,或者是爲了更方便地執行JAVA代碼】
利用OGNL進行導航的代碼以下:
//利用OGNL表達式訪問
String value = (String)Ognl.getValue("group.org.orgId", user);
Ognl.getValue()方法的第一個參數,就是一條OGNL表達式,第二個參數是指定在表達式中須要用到的root對象!
完整代碼以下:
public void testOgnl01() throws Exception{
User user = new User();
user.setUsername("張三");
//利用OGNL表達式訪問user對象的username屬性
String value = (String)Ognl.getValue("username", user);
log(value);
}
public void testOgnl02() throws Exception{
User user = new User();
Group g = new Group();
Organization o = new Organization();
o.setOrgId("ORGID");
g.setOrg(o);
user.setGroup(g);
//用JAVA來導航訪問
log(user.getGroup().getOrg().getOrgId());
//利用OGNL表達式訪問
String value = (String)Ognl.getValue("group.org.orgId", user);
log(value);
}
public void testOgnl03() throws Exception{
User user = new User();
Group g = new Group();
Organization o = new Organization();
o.setOrgId("ORGID");
g.setOrg(o);
user.setGroup(g);
//用JAVA來導航訪問
log(user.getGroup().getOrg().getOrgId());
//也能夠在表達式中使用#root來表明root對象
String value = (String)Ognl.getValue("#root.group.org.orgId", user);
log(value);
}
private void log(Object o){
System.out.println(o);
}
Context對象
在OGNL的表達式中,有可能須要訪問到多個絕不相干的對象,這時候,咱們須要給OGNL傳遞一個Map類型的對象,把表達式中須要用到的對象放到Map中便可!這個Map對象,稱爲context。
要在表達式中訪問到context中的對象,須要使用「#對象名稱」的語法規則。
如:
public void testOgnl04() throws Exception{
User user = new User();
user.setUsername("張三");
Group g = new Group();
Organization o = new Organization();
o.setOrgId("ORGID");
g.setOrg(o);
user.setGroup(g);
User user2 = new User();
user2.setUsername("李四");
/**
* 所謂context其實就是一個Map類型的對象。主要是由於在OGNL中,不支持多個root對象,那麼
* 若是須要在表達式中訪問更多絕不相干的對象時,只能經過一個Map來把這些對象統一傳遞給OGNL。
*/
Map context = new HashMap();
context.put("u1", user);
context.put("u2", user2);
//在表達式中需經過「#+對象的名稱」來訪問context中的對象
//若是表達式中沒有用到root對象,那麼能夠用任意一個對象表明root對象!
String value = (String)Ognl.getValue("#u1.username + ',' + #u2.username", context,new Object());
log(value);
}
public void testOgnl05() throws Exception{
User user = new User();
user.setUsername("張三");
Group g = new Group();
Organization o = new Organization();
o.setOrgId("ORGID");
g.setOrg(o);
user.setGroup(g);
User user2 = new User();
user2.setUsername("李四");
User user3 = new User();
user3.setUsername("王五");
Map context = new HashMap();
context.put("u1", user);
context.put("u2", user2);
//給OGNL傳遞root對象及context對象,以便解釋對應的表達式
String value = (String)Ognl.getValue("#u1.username + ',' + #u2.username + ',' + username", context,user3);
log(value);
}
利用OGNL表達式進行賦值
OGNL表達式也能夠用於賦值操做。
public void testOgnl06() throws Exception{
User user = new User();
//調用setValue()方法來進行賦值
//第一個參數:OGNL表達式
//第二個參數:root對象
//第三個參數:要賦的值
Ognl.setValue("username", user, "張三");
log(user.getUsername());
}
public void testOgnl07() throws Exception{
User user = new User();
Map context = new HashMap();
context.put("u", user);
//調用setValue()方法來進行賦值
//第一個參數:OGNL表達式
//第二個參數:context對象
//第三個參數:root對象
//第四個參數:要賦的值
Ognl.setValue("#u.username", context, new Object(), "張三");
log(user.getUsername());
}
public void testOgnl08() throws Exception{
User user = new User();
Map context = new HashMap();
context.put("u", user);
//利用賦值符號"="來進行賦值
Ognl.getValue("#u.username = '李四'", context, new Object());
log(user.getUsername());
}
public void testOgnl09() throws Exception{
User user1 = new User();
User user2 = new User();
Map context = new HashMap();
context.put("u1", user1);
context.put("u2", user2);
//在一個表達式中能夠用逗號分隔,同時執行多個表達式
Ognl.getValue("#u1.username = '李四',#u2.username='王五'", context, new Object());
log(user1.getUsername());
log(user2.getUsername());
}
利用OGNL調用對象的方法
//************************* OGNL調用對象的方法 *****************************//
public void testOgnl10() throws Exception{
User user = new User();
//若是是調用root對象的方法,能夠直接使用方法的名稱來調用方法
Integer value = (Integer)Ognl.getValue("addSomething(1,1)", user);
log(value);
}
public void testOgnl11() throws Exception{
User user = new User();
user.setUsername("李四");
//若是是調用root對象的方法,能夠直接使用方法的名稱來調用方法
String value = (String)Ognl.getValue("getUsername()", user);
log(value);
}
public void testOgnl12() throws Exception{
User user = new User();
Ognl.getValue("setUsername('王五')", user);
String value = (String)Ognl.getValue("getUsername()", user);
log(value);
}
//************************* OGNL調用靜態方法和變量 *********************//
public void testOgnl13() throws Exception{
User user = new User();
user.setUsername("王五");
//調用靜態變量
//注意:out是System中的靜態變量,out是PrintStream類型的一個對象
//而println()則是out這個對象中的實例方法(不是靜態方法)
//調用靜態方法,須要在類名和變量名前面加上@來調用,對於實例方法,用"."來調用
Ognl.getValue("@System @out.println(username)", user);
}
public void testOgnl14() throws Exception{
User user = new User();
user.setUsername("wangwu");
//調用靜態方法,注意使用全路徑類名
Ognl.getValue("@System @out.println(@cn.com.leadfar.utils.Utils@toUpperCase(username))", user);
}
利用OGNL訪問數組、集合對象
public void testOgnl15() throws Exception{
Object root = new Object();
Map context = new HashMap();
//利用OGNL建立java.util.List對象
List list = (List)Ognl.getValue("{123,'xxx','kdjfk'}", context, root);
context.put("list", list);
//利用OGNL建立數組
int[] intarray = (int[])Ognl.getValue("new int[]{23,45,67}", context, root);
context.put("intarray", intarray);
//利用OGNL表達式建立java.util.Map對象
Map mapvalue = (Map)Ognl.getValue("#{'listvalue':#list,'intvalue':#intarray}", context, root);
context.put("mapvalue", mapvalue);
//利用OGNL表達式訪問這些數組和集合對象
Ognl.getValue("@System @out.println(#list[1])", context,root);
Ognl.getValue("@System @out.println(#intarray[2])", context,root);
Ognl.getValue("@System @out.println(#mapvalue.listvalue[0])", context,root);
Ognl.getValue("@System @out.println(#mapvalue['intvalue'][0])", context,root);
}
public void testOgnl16() throws Exception{
List root = new ArrayList();
User user1 = new User();
user1.setUsername("張三");
User user2 = new User();
user2.setUsername("李四");
root.add(user1);
root.add(user2);
//若是root對象是List類型
log(Ognl.getValue("#root[0].username", root));
log(Ognl.getValue("#root[1].username", root));
}
更多的特性,請參考官方的文檔
OGNL官方文檔地址:http://www.opensymphony.com/ognl/html/LanguageGuide/index.html
應用:ValueStack
理解ValueStack的基本機制!對各類現象做出解釋。
ValueStack實際上就是對OGNL的封裝,OGNL主要的功能就是賦值與取值,Struts2正是經過ValueStack來進行賦值與取值的!
ValueStack是一個接口,而OgnlValueStack是strtus2中的缺省實現。ValueStack中的數據,分兩個部分存放:root和context(這與OGNL中的概念一致),同時ValueStack暴露相關的接口:
void setValue(String expr, Object value);
Object findValue(String expr);
用來經過OGNL表達式對ValueStack中的數據進行操做!
ValueStack中的root對象是CompoundRoot,CompoundRoot繼承了ArraryList,提供了額外的方法:push()和pop()方法,用來對root對象中所包含的數據進行存取!
public class CompoundRoot extends ArrayList {
public CompoundRoot() {
}
public CompoundRoot(List list) {
super(list);
}
public CompoundRoot cutStack(int index) {
return new CompoundRoot(subList(index, size()));
}
public Object peek() {
return get(0);
}
public Object pop() {
return remove(0);
}
public void push(Object o) {
add(0, o);
}
}
正是經過這兩個方法,CompoundRoot變成了一個棧結構!壓棧操做,將致使對象被放到CompoundRoot的第0個元素上(第0個元素 是棧頂),其它對象被依次日後移動;出棧操做,將致使CompoundRoot的第0個元素被移除(即棧頂元素被彈出),其它對象被依次往前移動!
OGNL不支持多個root對象,而struts2可以支持多個root對象,它對OGNL作了擴展。
若是某個OGNL表達式被傳遞給ValueStack(即調用ValueStack的setValue或findValue方法),而表達式中包含 有對root對象的訪問操做,ValueStack將依次從棧頂往棧底搜索CompoundRoot對象中所包含的對象,看哪一個對象具備相應的屬性,找到 以後,馬上返回。
在Struts2中,一個請求在最終到達Action的方法以前,Action對象自己會被壓入ValueStack(實際上就是放到ValueStack的CompoundRoot中),因此Action對象是CompoundRoot中的一個元素。看下面的代碼:
public class UserAction {
private String username;
private Integer age;
private boolean valid;
//查看用戶的詳細信息
public String detail(){
username = "張三";
age = 18;
valid = true;
return "detail";
}
在Action中,給Action的username/age/valid賦值。Detail頁面以下:
username:<s:property value="username"/> <br/>
valid:<s:property value="valid"/> <br/>
age:<s:property value="age"/> <br/>
上述JSP頁面將能正確將它們的值取出。<s:property value=」ognl表達式」/>。在s:property標籤中的OGNL表達式,最終會交給ValueStack來解釋。username就 是一個OGNL表達式,意思是調用root對象的getUsername()方法。Struts2將自動搜索CompoundRoot中有哪些元素(從第 0個元素開始搜索),檢測這些元素是否有getUsername()方法,若是第0個元素沒有getUsername()方法,將繼續搜索第一、二、 3……個元素是否有getUsername()方法。
在上面的例子中,CompoundRoot中只有一個對象,就是userAction對象,而這個對象中正好有getUsername()方法,因此,上述JSP代碼將可以將值正確取出。
再看下面的例子:
public class UserAction {
private String username;
private String name;
//查看用戶的詳細信息
public String detail(){
username = "張三";
name = "王五";
User u = new User();
u.setUsername("趙毅");
ActionContext.getContext().getValueStack().push(u);
return "detail";
}
在上面這個UserAction的代碼中,咱們直接調用 ActionContext.getContext().getValueStack().push()方法,把一個User對象(這個對象擁有 getUsername()和setUsername()方法)直接壓入到ValueStack中,這時候,在ValueStack的 CompoundRoot中將有兩個元素:第0個元素是剛剛壓入的user對象[趙毅],而第1個元素是userAction對象[張三],若是在JSP 中使用下面的表達式來取值:
<s:property value=」username」/> ,那麼輸出的值將是「趙毅」!道理上面已經講過了,struts2將會從第0個元素開始搜索CompoundRoot中的對象,第0個元素正是剛剛壓入的那個user對象!
若是在JSP中使用<s:property value=」name」/>來取值,將取出「王五」,由於第0個元素user對象沒有name屬性,因此,會繼續搜索第1個元素userAction對象,在這個對象中就有name屬性了!
再看下面的代碼:
public class UserAction {
private String username;
//查看用戶的詳細信息
public String detail(){
username = "張三";
List list = new ArrayList();
for(int i=0; i<10; i++){
User user = new User();
user.setUsername("User"+i);
list.add(user);
}
ActionContext.getContext().put("users", list);
User u = new User();
u.setUsername("趙毅");
ActionContext.getContext().getValueStack().push(u);
return "detail";
}
對應的JSP以下:
1: <s:property value="username"/> <br/>
2: <s:iterator value="#users">
3: <s:property value="username"/>
4: <s:property value="#root[2].username"/><br/>
5: </s:iterator>
6: <s:property value="username"/>
7: <s:property value="#root[1].username"/> <!-- 張三 -->
根據剛纔的示例,咱們知道,第1行的username是「趙毅」(由於JSP在執行這行代碼的時候,CompoundRoot中有兩個元素:第0個 是「user對象趙毅」,第1個是「userAction對象張三」),所以第1行的username將取出CompoundRoot中第0個元素的 username屬性:趙毅
第2行代碼是iterator標籤,只定義了一個value屬性,iterator標籤將循環訪問users這個List中的User對象,並把當 前循環的user對象壓入到CompoundRoot中!因此,在第3行和第4行代碼被執行的時候,CompoundRoot中總共有3個元素:第0個元 素是被iterator標籤壓入的當前循環的user對象;第1個元素是「user對象趙毅」;第2個元素是「userAction對象張三」,所以第3 行代碼的執行結果就是輸出「UserX」,即當前循環的user對象的username屬性!iterator標籤將會依次取出List中的user對 象,並不斷壓入/彈出user對象(每次循環,都將執行一遍壓入/彈出)。而第4行代碼取第2個元素的username屬性,即userAction對象 的username屬性:張三。
第5行代碼執行完成以後,在CompoundRoot中將剩下2個元素,與第2行代碼被執行以前同樣。因此,第6行代碼的輸出和第1行代碼的輸出結果是同樣的,而第7行代碼將取出userAction對象的username屬性:張三