1. 数据传输背后机制:ValueStack(值栈)

在这一切的背后,是因为有了ValueStack(值栈)!

ValueStack基础:OGNL

OGNL是Struts2中使用的一种表达式语言,它可以用于JSP的标签库中,以便能够方便的访问各种对象的属性;它用于界面将参数传递到Action(并进行类型转换)中;它还可以用于struts2的配置文件中!所以,非常有必要理解OGNL的基本机制。

Root对象

OGNL称为对象图导航语言。所谓对象图,即以任意一个对象为根,通过OGNL可以访问与这个对象关联的其它对象。如:

  1. package cn.com.leadfar.struts2.actions;
  2.  
  3. public class User {
  4. private String username;
  5. private Group group;
  6.  
  7. public String getUsername() {
  8. return username;
  9. }
  10. public void setUsername(String username) {
  11. this.username = username;
  12. }
  13.  
  14. public Group getGroup() {
  15. return group;
  16. }
  17. public void setGroup(Group group) {
  18. this.group = group;
  19. }
  20. }
  21.  
  22. package cn.com.leadfar.struts2.actions;
  23.  
  24. public class Group {
  25. private String name;
  26. private Organization org;
  27. public String getName() {
  28. return name;
  29. }
  30.  
  31. public void setName(String name) {
  32. this.name = name;
  33. }
  34.  
  35. public Organization getOrg() {
  36. return org;
  37. }
  38.  
  39. public void setOrg(Organization org) {
  40. this.org = org;
  41. }
  42. }
  43.  
  44. package cn.com.leadfar.struts2.actions;
  45.  
  46. public class Organization {
  47. private String orgId;
  48.  
  49. public String getOrgId() {
  50. return orgId;
  51. }
  52.  
  53. public void setOrgId(String orgId) {
  54. this.orgId = orgId;
  55. }
  56. }

上面三个类,描述了通过一个User对象,可以导航到Group对象,进而导航到Organization对象,以User对象为根,一个对象图如下所示:

User(root)

-- username

-- group

-- name

-- org

-- orgId

在真实的环境下,这个对象图可能会极其复杂,但是通过基本的getters方法,都应该能够访问到某个对象的其它关联对象。【对象图的导航,必须通过getters方法进行导航】,下述代码将创建一个User对象,及其相关的一系列对象:

  1. User user = new User();
  2. Group g = new Group();
  3. Organization o = new Organization();
  4. o.setOrgId("ORGID");
  5. g.setOrg(o);
  6. user.setGroup(g);

如果通过JAVA代码来进行导航(依赖于getters方法),导航到Organization的orgId属性,如下所示:

  1. //用JAVA来导航访问
  2. user.getGroup().getOrg().getOrgId();

【注意:导航的目的,是为了获取某个对象的值或设置某个对象的值或调用某个对象的方法!】

【注意:OGNL表达式语言的真正目的,是为了在那些不能写JAVA代码的地方执行JAVA代码,或者是为了更方便地执行JAVA代码】

利用OGNL进行导航的代码如下:

  1. //利用OGNL表达式访问
  2. String value = (String)Ognl.getValue("group.org.orgId", user);

Ognl.getValue()方法的第一个参数,就是一条OGNL表达式,第二个参数是指定在表达式中需要用到的root对象!

完整代码如下:

  1. public void testOgnl01() throws Exception{
  2. User user = new User();
  3. user.setUsername("张三");
  4.  
  5. //利用OGNL表达式访问user对象的username属性
  6. String value = (String)Ognl.getValue("username", user);
  7. log(value);
  8. }
  9.  
  10. public void testOgnl02() throws Exception{
  11. User user = new User();
  12. Group g = new Group();
  13. Organization o = new Organization();
  14. o.setOrgId("ORGID");
  15. g.setOrg(o);
  16. user.setGroup(g);
  17.  
  18. //用JAVA来导航访问
  19. log(user.getGroup().getOrg().getOrgId());
  20.  
  21. //利用OGNL表达式访问
  22. String value = (String)Ognl.getValue("group.org.orgId", user);
  23. log(value);
  24. }
  25.  
  26. public void testOgnl03() throws Exception{
  27. User user = new User();
  28. Group g = new Group();
  29. Organization o = new Organization();
  30. o.setOrgId("ORGID");
  31. g.setOrg(o);
  32. user.setGroup(g);
  33.  
  34. //用JAVA来导航访问
  35. log(user.getGroup().getOrg().getOrgId());
  36.  
  37. //也可以在表达式中使用#root来代表root对象
  38. String value = (String)Ognl.getValue("#root.group.org.orgId", user);
  39. log(value);
  40. }
  41. private void log(Object o){
  42. System.out.println(o);
  43. }

Context对象

在OGNL的表达式中,有可能需要访问到多个毫不相干的对象,这时候,我们需要给OGNL传递一个Map类型的对象,把表达式中需要用到的对象放到Map中即可!这个Map对象,称为context。要在表达式中访问到context中的对象,需要使用“#对象名称”的语法规则。如:

  1. public void testOgnl04() throws Exception{
  2. User user = new User();
  3. user.setUsername("张三");
  4. Group g = new Group();
  5. Organization o = new Organization();
  6. o.setOrgId("ORGID");
  7. g.setOrg(o);
  8. user.setGroup(g);
  9.  
  10. User user2 = new User();
  11. user2.setUsername("李四");
  12.  
  13. /**
  14. * 所谓context其实就是一个Map类型的对象。主要是因为在OGNL中,不支持多个root对象,那么
  15. * 如果需要在表达式中访问更多毫不相干的对象时,只能通过一个Map来把这些对象统一传递给OGNL。
  16. */
  17. Map context = new HashMap();
  18. context.put("u1", user);
  19. context.put("u2", user2);
  20.  
  21. //在表达式中需通过“#+对象的名称”来访问context中的对象
  22. //如果表达式中没有用到root对象,那么可以用任意一个对象代表root对象!
  23. String value = (String)Ognl.getValue("#u1.username + ',' + #u2.username", context,new Object());
  24. log(value);
  25. }
  26.  
  27. public void testOgnl05() throws Exception{
  28. User user = new User();
  29. user.setUsername("张三");
  30. Group g = new Group();
  31. Organization o = new Organization();
  32. o.setOrgId("ORGID");
  33. g.setOrg(o);
  34. user.setGroup(g);
  35.  
  36. User user2 = new User();
  37. user2.setUsername("李四");
  38.  
  39. User user3 = new User();
  40. user3.setUsername("王五");
  41.  
  42. Map context = new HashMap();
  43. context.put("u1", user);
  44. context.put("u2", user2);
  45.  
  46. //给OGNL传递root对象及context对象,以便解释对应的表达式
  47. String value = (String)Ognl.getValue("#u1.username + ',' + #u2.username + ',' + username", context,user3);
  48. log(value);
  49. }

利用OGNL表达式进行赋值

OGNL表达式也可以用于赋值操作。

  1. public void testOgnl06() throws Exception{
  2. User user = new User();
  3.  
  4. //调用setValue()方法来进行赋值
  5. //第一个参数:OGNL表达式
  6. //第二个参数:root对象
  7. //第三个参数:要赋的值
  8. Ognl.setValue("username", user, "张三");
  9.  
  10. log(user.getUsername());
  11. }
  12.  
  13. public void testOgnl07() throws Exception{
  14. User user = new User();
  15.  
  16. Map context = new HashMap();
  17. context.put("u", user);
  18.  
  19. //调用setValue()方法来进行赋值
  20. //第一个参数:OGNL表达式
  21. //第二个参数:context对象
  22. //第三个参数:root对象
  23. //第四个参数:要赋的值
  24. Ognl.setValue("#u.username", context, new Object(), "张三");
  25.  
  26. log(user.getUsername());
  27. }
  28.  
  29. public void testOgnl08() throws Exception{
  30. User user = new User();
  31.  
  32. Map context = new HashMap();
  33. context.put("u", user);
  34.  
  35. //利用赋值符号"="来进行赋值
  36. Ognl.getValue("#u.username = '李四'", context, new Object());
  37.  
  38. log(user.getUsername());
  39. }
  40.  
  41. public void testOgnl09() throws Exception{
  42. User user1 = new User();
  43. User user2 = new User();
  44. Map context = new HashMap();
  45. context.put("u1", user1);
  46. context.put("u2", user2);
  47.  
  48. //在一个表达式中可以用逗号分隔,同时执行多个表达式
  49. Ognl.getValue("#u1.username = '李四',#u2.username='王五'", context, new Object());
  50.  
  51. log(user1.getUsername());
  52. log(user2.getUsername());
  53. }

利用OGNL调用对象的方法

  1. //************************* OGNL调用对象的方法 *****************************//
  2. public void testOgnl10() throws Exception{
  3. User user = new User();
  4.  
  5. //如果是调用root对象的方法,可以直接使用方法的名称来调用方法
  6. Integer value = (Integer)Ognl.getValue("addSomething(1,1)", user);
  7. log(value);
  8. }
  9.  
  10. public void testOgnl11() throws Exception{
  11. User user = new User();
  12. user.setUsername("李四");
  13. //如果是调用root对象的方法,可以直接使用方法的名称来调用方法
  14. String value = (String)Ognl.getValue("getUsername()", user);
  15. log(value);
  16. }
  17.  
  18. public void testOgnl12() throws Exception{
  19. User user = new User();
  20. Ognl.getValue("setUsername('王五')", user);
  21. String value = (String)Ognl.getValue("getUsername()", user);
  22. log(value);
  23. }
  24.  
  25. //************************* OGNL调用静态方法和变量 *********************//
  26. public void testOgnl13() throws Exception{
  27. User user = new User();
  28. user.setUsername("王五");
  29. //调用静态变量
  30. //注意:out是System中的静态变量,out是PrintStream类型的一个对象
  31. //而println()则是out这个对象中的实例方法(不是静态方法)
  32. //调用静态方法,需要在类名和变量名前面加上@来调用,对于实例方法,用"."来调用
  33. Ognl.getValue("@System@out.println(username)", user);
  34. }
  35.  
  36. public void testOgnl14() throws Exception{
  37. User user = new User();
  38. user.setUsername("wangwu");
  39. //调用静态方法,注意使用全路径类名
  40. Ognl.getValue("@System@out.println(@cn.com.leadfar.utils.Utils@toUpperCase(username))", user);
  41. }

利用OGNL访问数组、集合对象

  1. public void testOgnl15() throws Exception{
  2.  
  3. Object root = new Object();
  4. Map context = new HashMap();
  5.  
  6. //利用OGNL创建java.util.List对象
  7. List list = (List)Ognl.getValue("{123,'xxx','kdjfk'}", context, root);
  8. context.put("list", list);
  9.  
  10. //利用OGNL创建数组
  11. int[] intarray = (int[])Ognl.getValue("new int[]{23,45,67}", context, root);
  12. context.put("intarray", intarray);
  13.  
  14. //利用OGNL表达式创建java.util.Map对象
  15. Map mapvalue = (Map)Ognl.getValue("#{'listvalue':#list,'intvalue':#intarray}", context, root);
  16. context.put("mapvalue", mapvalue);
  17.  
  18. //利用OGNL表达式访问这些数组和集合对象
  19. Ognl.getValue("@System@out.println(#list[1])", context,root);
  20. Ognl.getValue("@System@out.println(#intarray[2])", context,root);
  21. Ognl.getValue("@System@out.println(#mapvalue.listvalue[0])", context,root);
  22. Ognl.getValue("@System@out.println(#mapvalue['intvalue'][0])", context,root);
  23. }
  24.  
  25. public void testOgnl16() throws Exception{
  26.  
  27. List root = new ArrayList();
  28. User user1 = new User();
  29. user1.setUsername("张三");
  30. User user2 = new User();
  31. user2.setUsername("李四");
  32. root.add(user1);
  33. root.add(user2);
  34.  
  35. //如果root对象是List类型
  36. log(Ognl.getValue("#root[0].username", root));
  37. log(Ognl.getValue("#root[1].username", root));
  38. }

应用: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对象中所包含的数据进行存取!

  1. public class CompoundRoot extends ArrayList {
  2.  
  3. public CompoundRoot() {
  4. }
  5.  
  6. public CompoundRoot(List list) {
  7. super(list);
  8. }
  9.  
  10. public CompoundRoot cutStack(int index) {
  11. return new CompoundRoot(subList(index, size()));
  12. }
  13.  
  14. public Object peek() {
  15. return get(0);
  16. }
  17.  
  18. public Object pop() {
  19. return remove(0);
  20. }
  21.  
  22. public void push(Object o) {
  23. add(0, o);
  24. }
  25. }

正是通过这两个方法,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中的一个元素。看下面的代码:

  1. public class UserAction {
  2. private String username;
  3. private Integer age;
  4. private boolean valid;
  5.  
  6. //查看用户的详细信息
  7. public String detail(){
  8.  
  9. username = "张三";
  10. age = 18;
  11. valid = true;
  12.  
  13. return "detail";
  14. }

在Action中,给Action的username/age/valid赋值。Detail页面如下:

  1. username:<s:property value="username"/> <br/>
  2. valid:<s:property value="valid"/> <br/>
  3. 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代码将能够将值正确取出。

再看下面的例子:

  1. public class UserAction {
  2. private String username;
  3. private String name;
  4.  
  5. //查看用户的详细信息
  6. public String detail(){
  7. username = "张三";
  8. name = "王五";
  9.  
  10. User u = new User();
  11. u.setUsername("赵毅");
  12. ActionContext.getContext().getValueStack().push(u);
  13.  
  14. return "detail";
  15. }

在上面这个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属性了!

再看下面的代码:

  1. public class UserAction {
  2. private String username;
  3.  
  4. //查看用户的详细信息
  5. public String detail(){
  6. username = "张三";
  7.  
  8. List list = new ArrayList();
  9. for(int i=0; i<10; i++){
  10. User user = new User();
  11. user.setUsername("User"+i);
  12. list.add(user);
  13. }
  14. ActionContext.getContext().put("users", list);
  15.  
  16. User u = new User();
  17. u.setUsername("赵毅");
  18. ActionContext.getContext().getValueStack().push(u);
  19.  
  20. return "detail";
  21. }

对应的JSP如下:

  1. 1: <s:property value="username"/> <br/>
  2. 2: <s:iterator value="#users">
  3. 3: <s:property value="username"/>
  4. 4: <s:property value="#root[2].username"/><br/>
  5. 5: </s:iterator>
  6. 6: <s:property value="username"/>
  7. 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属性:张三

转自:http://blog.csdn.net/li_tengfei/article/details/6098134

Struts学习之ValueStack学习的更多相关文章

  1. (转)Predictive learning vs. representation learning 预测学习 与 表示学习

    Predictive learning vs. representation learning  预测学习 与 表示学习 When you take a machine learning class, ...

  2. java JDK8 学习笔记——助教学习博客汇总

    java JDK8 学习笔记——助教学习博客汇总 1-6章 (by肖昱) Java学习笔记第一章——Java平台概论 Java学习笔记第二章——从JDK到IDEJava学习笔记第三章——基础语法Jav ...

  3. [未完成]WebService学习第一天学习笔记

    [未完成]WebService学习第一天学习笔记[未完成]WebService学习第一天学习笔记

  4. Asp.net MVC4高级编程学习笔记-视图学习第一课20171009

    首先解释下:本文只是对Asp.net MVC4高级编程这本书学习记录的学习笔记,书本内容感觉挺简单的,但学习容易忘记,因此在边看的同时边作下了笔记,可能其它朋友看的话没有情境和逻辑顺序还请谅解! 一. ...

  5. 2019最新WEB前端开发小白必看的学习路线(附学习视频教程)

    2019最新WEB前端开发小白必看的学习路线(附学习视频教程).web前端自学之路:史上最全web学习路线,HTML5是万维网的核心语言,标准通用标记语言下的一个应用超文本标记语言(HTML)的第五次 ...

  6. 最近开始学习Cesium,学习学习。

    最近开始学习Cesium,学习学习.

  7. 新手如何学习Java——Java学习路线图

    推荐初学者阅读:新手如何学习Java——Java学习路线图

  8. python3.4学习笔记(七) 学习网站博客推荐

    python3.4学习笔记(七) 学习网站博客推荐 深入 Python 3http://sebug.net/paper/books/dive-into-python3/<深入 Python 3& ...

  9. 【深度学习】Pytorch学习基础

    目录 pytorch学习 numpy & Torch Variable 激励函数 回归 区分类型 快速搭建法 模型的保存与提取 批训练 加速神经网络训练 Optimizer优化器 CNN MN ...

随机推荐

  1. Android常用组件Broadcast介绍

    一.Broadcast简介 Broadcast是Android的四大组件之一.可分为: 1.普通广播 发送一个广播,所有监听该广播的广播接收者都可以监听到改广播. 2.异步广播 当处理完之后的Inte ...

  2. Lambda 表达式中的变量范围

    delegate bool D(); delegate bool D2(int i); class Test { D del; D2 del2; public void TestMethod(int ...

  3. 关于javascript 数组的正态分布排序的一道面试题

    最近几天顶着上海40°的凉爽天气找工作,心里是开心的不要不要的,每次面试都是要坐那里出半天汗才能回过神来,感觉到了这个世界对我深深的爱意,言归正传,面试过程中碰到了几次笔试,其中有这么一道题,由于实际 ...

  4. linux下C++ STL hash_map的使用以及使用char *型变量作为Key值的一大“坑”

    计算机编程中经常会用到hash表,而在C++中,使用STL编程更是少不了的.本文将介绍STL中hash_map的使用.在hash_map中使用自定义类型作为key值的方法以及在使用char *类型作为 ...

  5. Android进程

    android进程等级 1.前台进程 2.可视进程 3.服务进程 4.后台进程 5.空进程 让应用退出方式 finish();让应用成为空进程 Process.killProcess();杀进程 sy ...

  6. IE6、7 a链接内图片加滤镜后导致a标签链接失效问题解决

    今天在项目中遇到一个ie6.7浏览器下a链接失效的问题,查询大量资料,最终找到完美的解决方案,如下: 解决方法: 为a标签加样式{*background:url(#);*zoom:1;} 为img外部 ...

  7. Python学习(二) 运行Python,编译Python

    无论windos还是Linux只要安装了python,配置好了环境变量,则在命令行输入python这个命令的时候就会进入交互模式.在这个模式下可以进行一些简单的python代码编写.退出可以使用exi ...

  8. QT下的几种透明效果(三种方法:调色板,透明度属性,自绘)

    1.窗口整体透明,但是窗体上的控件不透明.    通过设置窗体的背景色来实现,将背景色设置为全透.  QPalette pal = palette();  pal.setColor(QPalette: ...

  9. rails跑通第一个demo

    rails -h 查看帮助 Usage: rails new APP_PATH [options] Options: -r, [--ruby=PATH] # Path to the Ruby bina ...

  10. C语言的本质(15)——C语言的函数接口入门

    C语言的本质(15)--C语言的函数接口 函数的调用者和其实现者之间存在一个协议,在调用函数之前,调用者要为实现者提供某些条件,在函数返回时,实现者完成调用者需要的功能. 函数接口通过函数名,参数和返 ...