Struts2数据传输的背后机制:ValueStack(值栈)
1.???? 數據傳輸背后機制:ValueStack(值棧)
?
在這一切的背后,是因為有了ValueStack(值棧)!
?
ValueStack基礎:OGNL
要了解ValueStack,必須先理解OGNL(Object Graphic Navigatino Language)!
OGNL是Struts2中使用的一種表達式語言,它可以用于JSP的標簽庫中,以便能夠方便的訪問各種對象的屬性;它用于界面將參數傳遞到Action(并進行類型轉換)中;它還可以用于struts2的配置文件中!所以,非常有必要理解OGNL的基本機制。
?
Root對象
OGNL稱為對象圖導航語言。所謂對象圖,即以任意一個對象為根,通過OGNL可以訪問與這個對象關聯的其它對象。如:
| package?cn.com.leadfar.struts2.actions; ? public?class?User { ????private?String username; ????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()方法,將繼續搜索第1、2、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屬性:張三
轉載于:https://www.cnblogs.com/baiduligang/p/4247550.html
總結
以上是生活随笔為你收集整理的Struts2数据传输的背后机制:ValueStack(值栈)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: sencha touch 组件选择器ge
- 下一篇: C#+Mapxtreme 实现一些GIS
