• 1. Struts2与OGNL王健
  • 2. 本章目标:OGNL表达式。 - 什么是OGNL表达式。 OGNL表达式,其实是一串特殊的字符串,通过这一串字符串可以快速的操作Java代码,甚至是不允许操作Java代码的地方,如JSP页面。 ValueStack中的Context和root 。 Struts2是如何通过ValueStack来操作OGNL的。 Struts2经常使用的几个标签 包: 如果只在Java代码中做测试,则只导入ognl.jar即可。
  • 3. 数据传输背后机制:ValueStack(值栈)要了解ValueStack,必须先理解OGNL(Object Graphic Navigatino Language)。 OGNL是Struts2中使用的一种表达式语言 它可以用于JSP的标签中,以便能够方便的访问各种对象的属性; 它用于界面将参数传递到Action(并进行类型转换)中; 它还可以用于struts2的配置文件中!所以,非常有必要理解OGNL的基本机制。 Root对象 OGNL称为对象图导航语言。所谓对象图,即以任意一个对象为root根,通过OGNL表达式可以访问与这个对象关联的其它对象。
  • 4. OGNL的目的:OGNL目的,是为了获取某个对象的值或设置某个对象的值或调用某个对象的方法! OGNL表达式语言的真正目的,是为了在那些不能写JAVA代码的地方执行JAVA代码,或者是为了更方便地执行JAVA代码。 Ognl.getValue()方法的第一个参数,就是一条OGNL表达式,第二个参数是指定在表达式中需要用到的root对象!
  • 5. OGNLOGNL必须拥有一个根root和一个OGNL表达式。可选拥有一个Map类型的Context。OGNLOGNL 表达式Context 可选new Object() 根必须OGNL表达式支持以下操作符:#、@、{}、‘’(单引号)等。
  • 6. #符的作用:访问根对象的属性或是方法不需要使用#,但如果非使用#来访问根对象的信息则必须使用#root,即与root关键字共同使用。 如果是调用非根对象的方法或属性必须要使用#符。
  • 7. Context对象在OGNL的表达式中,有可能需要访问到多个毫不相干的对象,这时候,我们需要给OGNL传递一个Map类型的对象,把表达式中需要用到的对象放到Map中即可!这个Map对象,称为context。 要在表达式中访问到context中的对象,需要使用“#对象名称”的语法规则。 所谓context其实就是一个Map类型的对象。主要是因为在OGNL中,不支持多个root对象,那么 如果需要在表达式中访问更多毫不相干的对象时,只能通过一个Map来把这些对象统一传递给OGNL。
  • 8. 只有Context没有根对象的示例:第一个参数为OGNL表达式。 第二个参数为Map类型的Context对象。 第三个参数为根对象,此根对象为一个空的对象。但必须要传。
  • 9. 同时给OGNL传递Context和root对象:注意是如何获取第三个对象中的值的:
  • 10. OGNL可以进行赋值操作:仅供了解: 第一个参数为ONGL表达式。 第二个参数为root对象。 第三个参数为设置的值。下图 第一个参数为:OGNL表达式, 第二个参数为根对象,第三个参数为值。下图 第一个参数为:OGNL表达式, 第二个参数为Context对象, 第三个参数为root对象。 第四个参数为值。
  • 11. 利用OGNL表达式调用对象的方法:以下代码通过两种方式调用,第一种方式调用的是非根对象的方法,第二种调用的是根对象的方法。
  • 12. @符的作用:在OGNL中,@符用来调用静态属性或是方法。当然必须是公共的。 使用@符默认调用的是Math中的静态方法,所以在第一种方法对Math类省略了。 第二种是书写完整的代码。
  • 13. 利用OGNL创建List和Map集合:以下创建List,第一个参数为OGNL表达式,第二个参数为root.以下创建Map,第一个参数为OGNL表达式,第二个参数为root.,注意#符的使用。
  • 14. 小结:通过上面的实现我们清楚的知道了两个问题: 1:OGNL就是用来操作属性和方法的。 2:OGNL操作的对像可以有两个 一个是根root对象,它只能有一个。 一个是Map的Context对象,它可以有多个。通过key,value来保存信息。 那么Struts2的又是如何使用OGNL的呢?
  • 15. 理解ValueStack的基本机制!对各种现象作出解释ValueStack实际上就是对OGNL的封装,OGNL主要的功能就是赋值与取值,Struts2正是通过ValueStack来进行赋值与取值的! ValueStack是一个接口,而OgnlValueStack是strtus2中的缺省实现。 ValueStack中的数据,分两个部分存放:root和context(这与OGNL中的概念一致)。 在ValueStack中有两个方法: - 见OgnlValueStack的源代码 getContext() – 返回非根对象,即一个Map. getRoot() – 返回root根对象。而它是一个List。所以,ValueStack可以有多个根对象。 ValueStack中的root对象是CompoundRoot,CompoundRoot继承了ArraryList,提供了额外的方法:push()和pop()方法,用来对root对象中所包含的数据进行存取!我们称为压栈和弹栈。
  • 16. CompoundRoot的源代码:正如右图所示: CompoundRoot 变成了一个栈结构! 压栈操作,将导致对象被 放到CompoundRoot的 第0个元素上(第0个 元素是栈顶), 其它对象被依次往后移动; 出栈操作,将导致 CompoundRoot的第0个元 素被移除(即栈顶元素被 弹出),其它对象被依 次往前移动!
  • 17. Struts2的数据标签,是通过直接操作ValueStack对象获取数据的:在Struts2中,一个请求在最终到达Action的方法之前,Action对象本身会被压入ValueStack(实际上就是放到ValueStack的CompoundRoot中),所以Action对象是CompoundRoot中的一个元素。 在Struts2中,操作ValueStack的就是struts2的自定义标签,即/struts-tags. 为了能说明问题,先让我们认识它的两个基本数据标签: s:property从ValueStack中获取属性输出。 s:inerator遍历集合,每次遍历都将数据压栈和弹栈,即操作CompoundRoot.图中的第二步即是将Action 压入栈顶。
  • 18. 以下演示请求一个Action时,Action对象位于ValueStack的栈顶: 第一步、1:在Struts2中,一个请求在最终到达Action的方法之前,Action对象本身会被压入ValueStack(实际上就是放到ValueStack的CompoundRoot中),所以Action对象是CompoundRoot中的一个元素。看下面的代码:
  • 19. 第一步、2:页面获取数据: JSP页面将能正确将它们的值取出。,意思是调用root对象的getName()方法。 Struts2将自动搜索CompoundRoot中有哪些元素(从第0个元素开始搜索),检测这些元素是否有getName()方法,如果第0个元素没有,将继续搜索第1、2、3……个元素是否有getName()方法。 如果在栈中没有找到getName方法,则再去ActionContext,即request的map中去查找,如果没有则输出空字符串。 注意,此时即使用户通过ActionContext.getContext().getSession().put(“name”,”someName”)也不会输出。因这它并不位于root中,而是位于Context中,对于位于Context中的信息则必须要使用#session.name的方式获取。
  • 20. 以下代码手工将自己封装的对象压入栈顶: 第二步:修改第一步的Action代码如下本身Action拥有一个name属性,然后又通过直接操作ValueStack将User类压入栈顶,所以,同样使用第一步2所示的代码,将返回“李四”,而 age值则为空。Age返回空的原因为是User这个对象中也有一个age属性。但如果age不是User的属性,则会再找第1..2直到N个元素,真到找不到为止。如果找到就输出它的值。
  • 21. 示例2:遍历Action代码如下:具有相同属性的对象被放到不同的域中
  • 22. 页面遍历:第8行显示的名称为:张三,这很显然,因为是从root中获取。 第10行依次显示Jack0..N,并不断的将信息压入栈顶,然后再删除。 第13行,如果不使用#root[1],则会与11行相同。 第16行,显示张三。因为,遍历完成以后,栈又回复到以前的状态。
  • 23. 示例3:当访问实现了ModelDriven接口的Action时在访问Action时,Struts2会做两个事 先将Action压入栈中。 再将Model,如User对像压入栈中。此时它位于栈顶。
  • 24. 页面获取示例3的值:
  • 25. 小结:通过以上分析,我们知道了: Struts2是通过标签直接与ValueStack打交道的。 在ValueStack中,保存着一个根对象Root,其实它就是一个List. 同时保存着一个Map,即Context非根对象。 如果一个Action实现了ModelDriven接口,此时会先将Action压入栈中,再将Model压入栈中。按后进先出的原则,Model对象则位于栈顶。
  • 26. Struts2的OGNL操作符再说明:OGNL要结合struts标签来使用。由于比较灵活,也容易把人给弄晕,尤其是“%”、“#”、“$”这三个符号的使用。由于$广泛应用于EL中,这里重点写%和#符号的用法。 %-用于说明内部是OGNL可执行的表达式,类似于eval. #-用于从某个范围中取出数据。 $-在struts的配置文件或资源文件中使用。 @-用于调用某个类的静态方法。 要打开此功能,请在struts.xml中添加以下配置: 以上所有必须要配合struts2的标签共同使用。
  • 27. @符操作静态成员和方法,必须是public的:
  • 28. “#”符号有三种用途:-1访问属性对像: (1)、访问非根对象(struts中值栈为根对象)如OGNL上下文和Action上下文,#相当于ActionContext.getContext(): 如:,是直接从ActionContext中取数据。 以下是获取其他数据: parameters     包含当前HTTP请求参数的Map     #parameters.id[0]作用相当于request.getParameter("id")  request     包含当前HttpServletRequest的属性(attribute)的Map     #request.userName相当于request.getAttribute("userName")     session     包含当前HttpSession的属性(attribute)的Map     #session.userName相当于session.getAttribute("userName")     application     包含当前应用的ServletContext的属性(attribute)的Map     #application.userName相当于application.getAttribute("userName")  attr 用于按request > session > application顺序访问其属性(attribute),#attr.userName相当于按顺序在以上三个范围(scope)内读取userName属性,直到找到为止。
  • 29. #”符号有三种用途:-2操作集合:构造Map,如: #{'foo1':'bar1', 'foo2':'bar2'} #{'foo1':'bar1', 'foo2':'bar2'}这种方式常用在给radio或select、checkbox等标签赋值上。如果要在页面中取一个map的值可以这样写:             
  • 30. %的作用:“%”符号的用途是在标签的属性值被理解为字符串类型时,告诉执行环境%{}里的是OGNL表达式。 很有点类似javascript里面的eval()功能。 以下将从map中获取一个key值。
  • 31. “$”的其他用途 在Struts 2配置文件中,引用OGNL表达式则应该使用$. listUser.action?msg=${msg}
  • 32. Struts2通用标签Struts2中通用标签可以分为二类:控制标签和数据标签。控制标签用于呈现页面时控制执行流程,数据标签用于访问值栈中的数据。 数据标签:用于访问ActionContext和值栈中的数据。数据标签包括: Property(重点) set push param bean action (重点) include-与普通的include一样使用 url (重点) a(超连接) i18n text date debug 控制标签:用于在呈现结果页面时控制程序的执行流程,根据程序执行的状态输出不同的结果,控制标签包括下列标签: if/elseif/else (重点) iterator (重点) append merge generator subset sort
  • 33. 数据标签-----property标签property标签用于输出值栈中的对象的属性(property)值,使用value属性来指定要输出的对象属性,如果没有指定value属性,那么默认输出栈顶对象。 property标签属性 在前面的章节中,我们已经使用过了property标签,我们再看一个例子: 取出栈顶对象(通常是action)的username属性并输出,如果没有找到username属性,那么输出”游客”。
  • 34. 数据标签-----action标签通过指定action的名字和可选的名称空间,action标签允许你在JSP中直接调用action。如果将标签的excuteResult属性设为true,那么action对应的结果输出也将被包含到本页面中。 在action标签的标签体中可以嵌套param标签,向action传递参数。 action标签的属性如果指定了id属性,则action将被放到OgnlContext中,在action标签结束后,可以通过#id来引用action.
  • 35. 控制标签-----if/elseif/else标签if/elseif标签属性test:为必填属性,是一个Boolean类型值,决定是否显示if标签内容。该标签标准格式如下: …….. …….. ………..
  • 36. 控制标签----- if/elseif/else标签举例
  • 37. 控制标签----- iterator(迭代标签)Iterator(迭代) 描述:用于遍历集合(java.util.Collection)List,Map,数组或枚举值(java.util.iterator)。该标签的属性如下表:
  • 38. IteratorStauts实例包含方法int getCount():返回当前迭代过元素的总数。 int getIndex():返回当前迭代元素的索引。 boolean isEven():判断当前迭元素是否为偶数。 boolean isOdd():判断当前迭元素是否为奇数。 boolean isFirst():判断当前迭元素是否为第一个元素。 boolean isLast():判断当前迭元素是否为最后一个元素 IteratorStauts的这些方法分别对应了count,index,even,odd,first,last属性
  • 39. 表单标签Struts2的表单标签可以分为两类,form标签本身和包装HTML表单元素的其他标签。form标签本身的行为不同于它内部的元素。 Struts2表单标签包括下列标签: form textfield password radio checkbox checkboxlist select doubleselect combobox optiontransferselect optgroup updownselect textarea hidden file label reset submit token head
  • 40. 表单标签的name和value属性很多表单标签(form标签除外)的name属性和value属性之间存在一个独特的关系。name属性除了为HTML表单元素指定名字,在表单提交时作为请求参数的名字外,同时它还映射到Action的属性。 在大多数情况下,name属性映射到一个简单的JavaBean属性,例如name属性的值为”postalCode”,在表单提交后,struts2框架将会调用Action的setPostalCode()方法来设置属性。 有的时候你希望在表单元素中显示Action属性的数据,这时就轮到value属性登场了,为value属性指定表达式”%{postalCode}”,这将会调用Action的getPostalCode()方法,并在表单中显示返回的数据,之后, 用户可以编辑这个值,然后重新提交它。
  • 41. form标签form标签输出一个HTML输入表单,此外,xhtml主题的form标签还输出表单元素外围的表格。我们一般将主题设置为simple.
  • 42. (本页无文本内容)
  • 43. textfield标签textfield标签输出一个HTML单行文本输入控件,等价于HTML代码: 看下面的代码:
  • 44. password标签password标签输出一个HTML口令输入控件,等价于HTML代码: 例子:
  • 45. select标签select标签输出一个HTML列表框,等价于HTML代码:
  • 46. select标签示例示例一: 在select标签的list属性中直接使用OGNL表达式创建了一个列表,列表中的每一项都将作为HTML列表框的一个选项。 示例二: 在select标签的list属性中直接使用OGNL表达式创建了一个Map。在这里要注意的是,Map的key是作为列表框选项的值,而Map中的value是作为列表框选项的内容。
  • 47. select标签示例示例三: Header选项主要用来越提示作用,因此应该将header选项的值(通过headerKey属性设置)设为无意义的值,例如此处的-1. 示例四: 使用emptyOption属性在header选项后添加一个空的选项。 multiple属性设为false,则只能从下拉列表中选择一个选项,如果设为true,则可以多选。
  • 48. optgroup标签optgroup标签作为select标签的子标签使用,用于创建选项组。你可以在select标签的标签体中使用一个或者多个optgroup标签,对选项进行逻辑分组。注意, optgroup标签本身不能嵌套。 例子: <%@ taglib prefix=“s” uri=“/struts-tags” %>
  • 49. radio标签radio标签输出一组HTML单选按钮,等价于一组HTML代码: 例子: <%@ taglib prefix="s" uri="/struts-tags" %>
  • 50. checkbox标签checkbox标签输出一个HTML复选框,等价于代码: checkbox标签创建一个value属性为true或者为false的复选框。你可以通过checkbox标签的fieldValue属性来指定创建的HTML复选框value属性的值。我们看下面的代码: 复选框的值是true还是false,是由fieldValue属性来控制的,而不是由通常的value属性来设置的。
  • 51. checkboxlist标签checkboxlist标签使用一个列表创建一系列复选框,属性设置与类似,只是创建的是HTML复选框。不同的是, checkboxlist标签是多选标签。 示例:
  • 52. submit标签示例例1:指定image类型的提交按钮,使用method属性。 上述代码在客户端浏览器中的输出如下: 注意生成的input元素的name属性。 例2:指定button类型的提交按钮,使用action和method属性。 上述代码在客户端浏览器中的输出如下: 注意button标签的name属性。 通过上面的例子可以看到name属性有二个前缀:method和action,这是struts2提供的一种特性,即使用一些预定义的前缀来命名一个按钮,通过按钮的名字来改变执行的行为。Struts2定义了4个前缀,如下: method-------method:login action--------action:userManager redirect------redirect:cancel.jsp redirection-action------redirect-action:register
  • 53. method前缀使用method前缀,来取代action默认的execute()方法的执行。例如一个表单同时用于用户注册和登录,那么可以编写代码如下: 注意:1)对于input类型的提交按钮,不能通过submit标签的label属性来设置提交按钮上的文本,只能使用value属性。 2)可以在submit标签的name属性中直接使用method前缀,也可以像前面给出的例子中使用submit标签的method属性来指定处理请求的action方法。
  • 54. action前缀使用action前缀,取代form标签指定的action,将请求导向到另外的action进行处理。 示例: 如果用户已经注册,可以直接登录,否则单击注册按钮,转到注册页面。action前缀也可以和struts2的动态方法调用结合使用,例如:name=“action:userManager!register”.
  • 55. redirect前缀使用redirect前缀将请求重定向到其他的URL,甚至可以是Web应用程序外部的URL。 示例:
  • 56. redirect-action前缀使用redirect-action前缀将请求重定向到其他的action。在内部,struts2使用ServletRedirectResult来执行这个任务。 示例:
  • 57. token标签与file标签token标签输出两个隐藏的表单字段,用于防止表单的重复提交。要让token标签正常工作,需要启用TokenInterceptor或者TokenSessionInterceptor拦截器。 示例: 具体使用后面章节讲解。 file标签输出一个HTML文件选择框,等价于HTML代码:. 示例:
  • 58. Token防止重复提交:第一步:在页面上使用标签。
  • 59. Token防止重复提交:第二步:在配置文件中配置token拦截器: 需要说明的是:当验证不成功以后,拦截器会返回到一个名称叫invalid.token所指的页面上去。 你的数据已经提交,请不要多次提交。
  • 60. S:debug标签:
  • 61. Struts2总结:为什么要有struts2? 因为我们在做Web开发时总是做一些重复的工作,有了struts2之后,就由它的帮我们做这些重复的工作。我们只要关心业务逻辑,即Action就可以了。 对于ValueStack ActionContext.getContext.put是操作Map ActionContext.getValueStack().push、pop是操作栈。