`
livia1234
  • 浏览: 43902 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

在Struts2中ValueStack、ActionContext、ServletContext、request、session关系分析

 
阅读更多

1. ValueStack

ValueStack在中文版的《Struts2深入浅出》一书中译作“值栈”。其本身数据结构是一个栈,使用者可以把一些对象(又称作bean)存入值栈中,然后使用动态的表达式来读取bean的属性,或者对bean进行一些其他操作。由于值栈中可能有多个bean,值栈会按bean出栈的顺序依次尝试使用动态的表达式来读取值,直到成功读取值为止。在Struts2中,默认的值栈实现是OgnlValueStack,即默认使用Ognl这个动态表达式语言来读取值。

在Struts2执行一次请求的过程中,Struts2会把当前的Action对象自动放入值栈。这样,在渲染JSP时,JSP里的代码使用<s:property value="..."/>之类标签中的Ognl表达式会直接作用于Action对象,从而方便的读取Action的属性。

如何得到值栈:
  • 在自定义的拦截器中,使用ActionInvocation.getStack()方法( ActionInvocation 是拦截器的方法参数)。
  • 在Action类中,让拦截器注入ValueStack或者使用ActionContext.getContext().getValueStack()来值栈(ActionContext.getContext()为静态方法)。注意:ActionContext分配context的方式是基于线程的,如果使用这种方法,请确保它不会出错。
  • 在JSP中,直接使用标签即可获得值栈里的数据,而一般不用获取值栈本身。
如何将对象存入值栈:
  • Struts2自动存入Action:之前已经提到,Struts2在执行一次请求的过程中会把当前的Action对象自动存入值栈中。
  • ModelDrivenInterceptor会存入Action的model属性:如果你使用了Struts2提供的 ModelDrivenInterceptor,则它会把Action对象的getModel()方法得到的对象存入值栈中。此时,值栈最底层为Action类,其次为这个model。
  • 在自定义的拦截器中存入值栈:得到值栈对象后调用ValueStack.put(Object object)方法。
  • 在Action类中存入值栈:得到值栈对象后调用ValueStack.put(Object object)方法。
  • 在JSP中存入值栈:标签<s:push value="..."></s:push>是专门用来在JSP中把指定的value放入值栈的,但value被放入值栈的时间仅在s:push标签内,即程序运行到</s:push>标签处会把value从值栈中移出。另外,还有一些标签比如<s:iterator/>由于其功能的需要也会把一些对象放到值栈中。
让值栈执行表达式来获得值:
  • 在自定义的拦截器中,获得值栈后,使用ValueStack.findValue(...)等方法。
  • 在Action类中,获得值栈后,使用ValueStack.findVlaue(...)等方法。
  • 在JSP中,一些标签的属性是直接在值栈上执行Ognl表达式的,比如<s:property/>的value属性。如果标签的属性不是直接执行Ognl表达式的,则需要使用“%{}”将表达式括起来,这样Struts2就会以Ognl表达式来执行了。至于到底哪些标签是直接执行Ognl而哪些不是,请参考完整的官方文档
在JSP中跳过栈顶元素直接访问第二层:
  • 在JSP中,使用[0]、[1]等表达式来指定从栈的第几层开始执行表达式。[0]表示从栈顶开始,[1]表示从栈的第二层开始。比如表达式“name”等价于“[0].name”。参见此处。
在JSP中访问值栈对象本身(而不是它们的属性)
  • 在表示式中使用top关键字来访问对象本身。比如,表达式“name”等价于“top.name”,表达式“[0].top”等价于“top”,表达式“[1].top.name”等价于“[1].name”。

总之,值栈主要目的是为了让JSP内能方便的访问Action的属性。


一些例子:
  1. //此类为一个封装数据的简单类,在下面的例子会用到
  2. publicclassPerson{
  3. privateStringname;
  4. publicStringgetName(){
  5. returnname;
  6. }
  7. publicvoidsetName(Stringname){
  8. this.name=name;
  9. }
  10. }
Java代码复制代码收藏代码
  1. //本类将演示拦截器中对值栈的操作
  2. publicclassMyInterceptorextendsAbstractInterceptor{
  3. publicStringintercept(ActionInvocationinvocation)throwsException{
  4. //获得值栈
  5. ValueStackvalueStack=invocation.getStack();
  6. //存入值
  7. Personperson=newPerson();
  8. valueStack.push(person);
  9. //执行表达式获取值
  10. Stringname=(String)valueStack.findValue("name");
  11. //其他代码
  12. returninvocation.invoke();
  13. }
  14. }

Java代码复制代码收藏代码
  1. //本类将演示在Action中对值栈进行操作
  2. publicclassMyActionextendsActionSupport{
  3. @Override
  4. publicStringexecute()throwsException{
  5. //获得值栈
  6. ValueStackvalueStack=ActionContext.getContext().getValueStack();
  7. //存入值
  8. Personperson=newPerson();//这是之前例子中定义的类
  9. valueStack.push(person);
  10. //执行表达式获取值
  11. Stringname=(String)valueStack.findValue("name");
  12. //其他代码
  13. //......
  14. returnSUCCESS;
  15. }
  16. //以下定义的属性供接下来的JSP例子使用
  17. privateStringmessage;
  18. privatePersonperson;
  19. privateList<Person>personList;
  20. publicStringgetMessage(){
  21. returnmessage;
  22. }
  23. publicPersongetPerson(){
  24. returnperson;
  25. }
  26. publicList<Person>getPersonList(){
  27. returnpersonList;
  28. }
  29. }
  1. <%@pagecontentType="text/html"pageEncoding="UTF-8"%>
  2. <%@tagliburi="/struts-tags"prefix="s"%>
  3. <!DOCTYPEhtml>
  4. <html>
  5. <head>
  6. <metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">
  7. <title>JSPPage</title>
  8. </head>
  9. <body>
  10. <!--本JSP将演示在JSP中对值栈的使用-->
  11. <!--本JSP为MyAction对应的JSP-->
  12. <!--由于Action已经被存入的值栈,所以可以调用Action的属性-->
  13. <!--使用下面的标签和表达式来显示MyAction的message属性-->
  14. <s:propertyvalue="message"/>
  15. <!--使用下面的标签和表达式来调用Action的getText(...)方法,参数为MyAction的message属性-->
  16. <s:propertyvalue="getText(message)"/>
  17. <!--默认情况下传递给cssClass的是字符串常量。可以使用“%{}”来启用Ognl,这样,传递给cssClass的就不是字符串常量"message",而是上面所说的message的值-->
  18. <s:divcssClass="%{message}"/>
  19. <!--使用s:push标签来将对象放入值栈,如下-->
  20. <s:pushvalue="person">
  21. <!--在此s:push标签内,值栈的栈顶元素为person,栈顶第二层为action
  22. <!--在标签内直接调用person的属性(而不是Action的属性),如下-->
  23. <s:propertyvalue="name"/>
  24. <!--在标签内也可以使用MyAction的属性,值栈会依次先查找Person是否有该属性,由于没找到,会再MyAction中再查找,如下-->
  25. <s:propertyvalue="message"/>
  26. <!--可以使用“[0]”、“[1]”等指定从值栈的哪一层开始查找-->
  27. <!--此时,使用“[0]”表示从Person开始查找,当然还是找不到,值栈就接着到MyAction中查找,如下-->
  28. <s:propertyvalue="[0].message"/>
  29. <!--此时,使用“[1]”将从MyAction开始查找,而跳过了person,如下-->
  30. <s:propertyvalue="[1].message"/>
  31. <!--想要访问栈顶元素本身使用关键字“top”,比如,下面的top就代表栈顶的person,如下-->
  32. <s:propertyvalue="top"/>
  33. <!--或者如下-->
  34. <s:propertyvalue="[0].top"/>
  35. <!--想要访问MyAction本身的话使用如下写法-->
  36. <s:propertyvalue="[1].top"/>
  37. </s:push>
  38. <!--此时person已被移出值栈,再使用如下标签和表达式将无法得到值-->
  39. <!--<s:propertyvalue="name"/>-->
  40. <!--iterator标签会把list的每个元素依次存入栈顶,如下-->
  41. <s:iteratorvalue="personList">
  42. <!--获得List每个元素中的name属性-->
  43. <s:propertyvalue="name"/>
  44. </s:iterator>
  45. </body>
  46. </html>

2.ActionContext

ActionContext是Action的上下文,Struts2自动在其中保存了一些在Action执行过程中所需的对象,比如session, parameters, locale等。Struts2会根据每个执行HTTP请求的线程来创建对应的ActionContext,即一个线程有一个唯一的ActionContext。因此,使用者可以使用静态方法ActionContext.getContext()来获取当前线程的ActionContext,也正是由于这个原因,使用者不用去操心让Action是线程安全的。

无论如何,ActionContext都是用来存放数据的。Struts2本身会在其中放入不少数据,而使用者也可以放入自己想要的数据。ActionContext本身的数据结构是映射结构,即一个Map,用key来映射value。所以使用者完全可以像使用Map一样来使用它,或者直接使用Action.getContextMap()方法来对Map进行操作。

Struts2本身在其中放入的数据有ActionInvocation、application(即ServletContext)、conversionErrors、Locale、action的name、request的参数、HTTP的Session以及值栈等。完整的列表请参考它的Javadoc(本文附录有对它包含内容的讨论)。

由于ActionContext的线程唯一和静态方法就能获得的特性,使得在非Action类中可以直接获得它,而不需要等待Action传入或注入。需要注意的是,它仅在由于request而创建的线程中有效(因为request时才创建对应的ActionContext),而在服务器启动的线程中(比如fliter的init方法)无效。由于在非Action类中访问其的方便性,ActionContext也可以用来在非Action类中向JSP传递数据(因为JSP也能很方便的访问它)。

ValueStack与ActionContext的联系和区别:
  • 相同点:它们都是在一次HTTP请求的范围内使用的,即它们的生命周期都是一次请求。
  • 不同点:值栈是栈的结构,ActionContext是映射(Map)的结构。
  • 联系:ValueStack.getContext()方法得到的Map其实就是ActionContext的Map。查看Struts2的源代码可知(Struts2.3.1.2的org.apache.struts2.dispatcher.ng.PrepareOperations的第79行,createActionContext方法),在创建ActionContext时,就是把ValueStack.getContext()作为ActionContext的构造函数的参数。所以,ValueStack和ActionContext本质上可以互相获得。
  • 注意:在一些文档中,会出现把对象存入“stack's context”的字样,其实就是把值存入了ActionContext。所以在阅读这些文档时,要看清楚,到底是放入了栈结构(即值栈),还是映射结构(值栈的context,即ActionContext)。
如何获得ActionContext:
  • 在自定义的拦截器中:使用ActionInvocation.getInvocationContext()或者使用ActionContext.getContext()。
  • 在Action类中:让拦截器注入或者使用ActionContext.getContext()。
  • 在非Action类中:让Action类传递参数、使用注入机制注入或者使用ActionContext.getContext()。注意:只有运行在request线程中的代码才能调用ActionContext.getContext(),否则返回的是null。
  • 在JSP中:一般不需要获得ActionContext本身。
如何向ActionContext中存入值:
  • 在拦截器、Action类、非Action类等Java类中:使用ActionContext.put(Object key, Object value)方法。
  • 在JSP中:标签<s:set value="..."/>默认将值存入ActionContext中(当然,<s:set>标签还可以把值存到其他地方)。另外,许多标签都有var属性(以前用的是id属性,现在id属性已被弃用),这个属性能向ActionContext存入值,key为var属性的值,value为标签的value属性的值。(有些文档写的是向ValueStack的context存入值,其实是一样的)
如何从ActionContext中读取值:
  • 在拦截器、Action类、非Action类等Java类中:使用ActionContext.get(Object key)方法。
  • 在JSP中:使用#开头的Ognl表达式,比如<s:property value="#name"/>会调用ActionContext.get("name")方法。注意:如果某标签的属性默认不作为Ognl表达式解析,则需要使用%{}把表达式括起来,于是就会出现类似“%{#name}的表达式”。(“#”的更多用途参见这里
总之,在JSP中使用ActionContext一方面是由于它是映射结构,另一方面是能读取Action的一些配置。当你需要为许多Action提供通用的值的话,可以让每个Action都提供getXXX()方法,但更好的方法是在拦截器或JSP模板中把这些通用的值存放到ActionContext中(因为拦截器或JSP模板往往通用于多个Action)。

一些例子:
  1. //本类将演示拦截器中对ActionContext的操作
  2. publicclassMyInterceptorextendsAbstractInterceptor{
  3. publicStringintercept(ActionInvocationinvocation)throwsException{
  4. //获得ActionContext
  5. ActionContextactionContext=invocation.getInvocationContext();
  6. //存入值
  7. Personperson=newPerson();
  8. actionContext.put("person",person);
  9. //获取值
  10. Objectvalue=actionContext.get("person");
  11. //获取HttpServletRequest
  12. HttpServletRequestrequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST);
  13. //获取request的Map,即HttpServletRequest.getAttribute(...)和HttpServletRequest.setAttribute(...)所操作的值
  14. MaprequestMap=(Map)actionContext.get("request");
  15. //其他代码
  16. //......
  17. returninvocation.invoke();
  18. }
  19. }
Java代码复制代码收藏代码
  1. //本类将演示在Action中对ActionContext进行操作
  2. publicclassMyActionextendsActionSupport{
  3. @Override
  4. publicStringexecute()throwsException{
  5. //获得值栈
  6. ActionContextactionContext=ActionContext.getContext();
  7. //存入值
  8. Personperson=newPerson();//这是之前例子中定义的类
  9. actionContext.put("person",person);
  10. //获取值
  11. Objectobject=actionContext.get("person");
  12. //其他代码
  13. //......
  14. returnSUCCESS;
  15. }
  16. }
Html代码复制代码收藏代码
  1. <!DOCTYPEhtml>
  2. <html>
  3. <head>
  4. <metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">
  5. <title>JSPPage</title>
  6. </head>
  7. <body>
  8. <!--本JSP将演示在JSP中对ActionContext的使用-->
  9. <!--本JSP为MyAction对应的JSP-->
  10. <!--由于Action中已经向ActionContext存入了key为"person"的值,所以可以使用“#person”来获取它,如下-->
  11. <s:propertyvalue="#person"/>
  12. <!--获得person的name属性,如下-->
  13. <s:propertyvalue="#person.name"/>
  14. <!--获得Struts2在ActionContext中存入的值,比如request的Map,如下-->
  15. <s:propertyvalue="#request"/>
  16. <!--获得Struts2在ActionContext中存入的值,比如session的Map,如下-->
  17. <s:propertyvalue="#session"/>
  18. <!--获得Struts2在ActionContext中存入的值,request请求传递的GET参数或POST参数的Map,如下-->
  19. <s:propertyvalue="#parameters"/>
  20. <!--以下演示在JSP中把值存入ActionContext中-->
  21. <!--存入一个字符串"myName",key为"myKey",如下-->
  22. <s:setvalue="%{'myName'}"var="myKey"/>
  23. <!--使用s:bean标签来创建一个对象,并把它存入ActionContext中,key为myObject,如下-->
  24. <s:beanname="com.example.Person"var="myObject"/>
  25. <!--之后就可以用“#”来读取它们,如下-->
  26. <s:propertyvalue="#myKey"/>
  27. <s:propertyvalue="#myObject"/>
  28. </body>
  29. </html>

3. HttpServletRequest类或request的Map

Struts2中提供了两种对request的操作:一种是Web服务器提供的HttpServletRequest类,这和传统Java Web项目中的操作request的方式相同;另一种是一个“request的Map”,即封装了HttpServletRequest的attributes的映射类,操作该Map相当于操作HttpServletRequest的attributes。之所以提供了Map的操作方式,一是方便操作,二是能方便使用Ognl在JSP标签中读取request。无论如何,这两个request是互通的。至于request的生命周期等概念,与其他的Java Web项目没有区别,本文不再详述。

使用HttpServletRequest类还是request的Map
  • 虽然两者是互通的,但就读取request的attributes而言,使用request的Map要方便许多,并且不会暴露不必要的接口。当然,HttpServletRequest有一些request的Map没有的方法,使用这些方法时当然还是要用前者
使用request的Map还是ActionContext:
  • 两者都是Map,两者的生命周期都是一个请求。
  • 传统的Java Web项目中,往往是通过request的attributes来向JSP传递值的:先在Servlet里setAttribute(),然后在JSP里getAttribute()。当然在Struts2的项目中,你仍然可以使用这个方法,然而抛弃了Struts2提供的传递功能是得不偿失的。虽然笔者没有找到官方文档说一定要用ActionContext替换request的Map,也没有发现程序中有能获得ActionContext却获得不了request的Map的地方,但在Struts2框架下,操作ActionContext要比操作request的Map更加方便。因此,笔者建议:尽量使用ActionContext而不是request的Map来传递值
  • request的Map有时候会包含其他框架设置的值,比如Spring框架。获取这些值的时候就需要用request的Map了,因为ActionContext里没有。
  • 通过ActionContext可以获得HttpServletRequest类:“HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST);”。
  • 通过ActionContext也可以获得request的Map:“Map requestMap = (Map) actionContext.get("request");”。因此,在JSP标签中,使用表达式“#request”就可以获得request的Map的数据。
如何获得HttpServletRequest:
  • 如果已经有ActionContext,则使用“actionContext.get(StrutsStatics.HTTP_REQUEST)”来获得HttpServletRequest。
  • 在自定义的拦截器中,先获得ActionContext,再通过ActionContext来获得。
  • 在Action中,先获得ActionContext,再通过ActionContext来获得。或者让Action实现ServletRequestAware接口,并使用ServletConfigInterceptor拦截器,这样这个拦截器就会注入HttpServletRequest。
  • 在JSP中,一般不需要获得HttpServletRequest。
如何获得request的Map:
  • 如果已经有ActionContext,则使用“actionContext.get("request")”来获得。
  • 在自定义的拦截器中,先获得 ActionContext,再通过ActionContext来获得。
  • 在Action中,先获得ActionContext,再通过ActionContext来获得。或者让Action实现RequestAware接口,并使用ServletConfigInterceptor拦截器,这样这个拦截器就会注入Map request。
  • 在JSP中,用“#request”来获得request的Map,用“#request.key”或者“#request['key']”来读取Map中的值。

总之,request仍然符合Java Web网站的一般规律。不过笔者建议使用者应尽量避免用request传值。

一些例子:

Java代码复制代码收藏代码
  1. //本类将演示拦截器中对HttpServletRequest和request的Map的操作
  2. publicclassMyInterceptorextendsAbstractInterceptor{
  3. publicStringintercept(ActionInvocationinvocation)throwsException{
  4. //获得ActionContext
  5. ActionContextactionContext=invocation.getInvocationContext();
  6. //获得HttpServletRequest
  7. HttpServletRequesthttpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST);
  8. //获得request的Map
  9. MaprequestMap=(Map)actionContext.get("request");
  10. //创建一个类作为实例
  11. Personperson=newPerson();
  12. //以下两行的语句作用相同
  13. httpServletRequest.setAttribute("person",person);
  14. requestMap.put("person",person);
  15. //其他代码
  16. //......
  17. returninvocation.invoke();
  18. }
  19. }

Java代码复制代码收藏代码
  1. //本类将演示在Action中对HttpServletRequest和request的Map进行操作(静态方法获得ActionContext)
  2. publicclassMyActionextendsActionSupport{
  3. @Override
  4. publicStringexecute()throwsException{
  5. //获得ActionContext
  6. ActionContextactionContext=ActionContext.getContext();
  7. //获得HttpServletRequest
  8. HttpServletRequesthttpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST);
  9. //获得request的Map
  10. MaprequestMap=(Map)actionContext.get("request");
  11. //创建一个类作为实例
  12. Personperson=newPerson();
  13. //以下两行的语句作用相同
  14. httpServletRequest.setAttribute("person",person);
  15. requestMap.put("person",person);
  16. //其他代码
  17. //......
  18. returnSUCCESS;
  19. }
  20. }

Java代码复制代码收藏代码
  1. //本类将演示在Action中使用ServletRequestAware获得HttpServletRequest(注意:要使用ServletConfigInterceptor拦截器)
  2. publicclassMyActionextendsActionSupportimplementsServletRequestAware{
  3. privateHttpServletRequestrequest;
  4. //此方法是接口ServletRequestAware的方法
  5. publicvoidsetServletRequest(HttpServletRequestrequest){
  6. this.request=request;
  7. }
  8. @Override
  9. publicStringexecute()throwsException{
  10. //HttpServletRequest已在该类的字段中准备好,可直接使用
  11. //......
  12. returnSUCCESS;
  13. }
  14. }
Java代码复制代码收藏代码
  1. //本类将演示在Action中使用ServletRequestAware获得request的Map(注意:要使用ServletConfigInterceptor拦截器)
  2. publicclassMyActionextendsActionSupportimplementsRequestAware{
  3. Map<String,Object>request;
  4. //该方法是接口RequestAware的方法
  5. publicvoidsetRequest(Map<String,Object>request){
  6. this.request=request;
  7. }
  8. @Override
  9. publicStringexecute()throwsException{
  10. //request的Map已在该类的字段中准备好,可直接使用
  11. //......
  12. returnSUCCESS;
  13. }
  14. }

Java代码复制代码收藏代码
  1. <!DOCTYPEhtml>
  2. <html>
  3. <head>
  4. <metahttp-equiv="Content-Type"content="text/html;charset=UTF-8">
  5. <title>JSPPage</title>
  6. </head>
  7. <body>
  8. <!--本JSP将演示在JSP中对request的Map的使用-->
  9. <!--本JSP为MyAction对应的JSP-->
  10. <!--request的Map是Struts2自动在ActionContext中存入的值(key为request),所以使用“#”来访问ActionContext,从中读取request-->
  11. <s:propertyvalue="#request"/>
  12. <!--以下两行均是访问request的Map中key为“name”的值-->
  13. <s:propertyvalue="#request.name"/>
  14. <s:propertyvalue="#request['name']"/>
  15. </body>
  16. </html>

3. Parameters,即GET请求或POST请求的参数

Parameters为GET或POST等请求时浏览器向服务器传递而来的参数。在传统的Java Web项目中,使用HttpServletRequest.getParameter()等方法来获取参数,并且可以直接使用HttpServletRequest.getParameterMap()来获得一个封装了参数的Map。而在Struts2中,Struts2直接把上述Map存放到了ActionContext中,key为“parameters”。另外,ActionContext还直接提供了ActionContext.getParameters()方法来获得这个Map。因此,在Struts2的各个部件中操作parameters的方法和操作request的Map的方法十分相似,本段不再详述。

4. HttpServletSession类和session的Map

传统Java Web项目中的session是我们都熟悉的,我们用它来记录一个用户的会话状态。Struts2把HttpServletSession封装到了一个Map中,即“session的Map”,这类似对request的处理。然而为了节省系统资源,我们在不需要session的时候不会创建session。可能正是因为这个缘故,Struts2中没有把HttpServletSession放入ActionContext中,如果你的程序需要使用HttpServletSession,应该先获得HttpServletRequest,然后使用getSession()或getSession(boolean b)来获得它,同时决定是否需要创建session。对于session的Map,Struts2仍然把它放入了ActionContext中(key为"session"),但是不要担心,这个Map的机制使得只有put新值时才会创建session。总之,Struts2中对HttpServletSession的操作要先通过HttpServletRequest来获得它,而对session的Map的操作与对request的Map的操作如出一辙,本段不再详述。

5. ServletContext和application的Map

传统的Java Web项目中,ServletContext用来存放全局变量,每个Java虚拟机每个Web项目只有一个ServletContext。这个ServletContext是由Web服务器创建的,来保证它的唯一性。ServletContext有一些方法能操作它的attributes,这些操作方法和操作一个Map类似。于是,Struts2又来封装了:它把ServletContext的attributes封装到了一个Map中,即“application的Map”,并且也放入的ActionContext中(key为application),因此,对application的Map的操作就如果对request的Map操作,本段不再详述。

至于对ServletContext的操作,与HttpServletRequest的操作类似:Struts2将ServletContext放到了 ActionContext中,并且ServletConfigInterceptor提供了对ServletContext的注入接口ServletContextAware。因此,本段不再详述。

注意:在Ognl表达式中使用“#application”可以得到application的Map,而不是ServletContext。然而在JSP嵌入的Java代码中(比如“<% application.getAttribute(""); %>”),application为ServletContext,而不是Map。

用一张表格来总结:

变量 从ActionContext中获得 生命周期 用Ongl来读取值 使用ServletConfigInterceptor来注入
ActionContext类 静态方法ActionContext. getContext() 一次Http请求 使用“#”加上key,如“#name” 无法注入
ValueStack类 ActionContext. getValueStack() 一次Http请求 直接填写来访问栈中对象的方法,或者使用top来直接获得栈中对象 无法注入
HttpServletRequest类 ActionContext. get( StrutsStatics. HTTP_REQUEST) 一次Http请求 无方便的方法 实现ServletRequestAware接口
request的Map ActionContext. get("request") 一次Http请求 使用“#request”再加上key,如“#request.name”或者“#request['name']” 实现RequestAware接口
parameters的Map ActionContext. get( "parameters") 一次Http请求 使用“# parameters”再加上key,如“#parameters .name”或者“#parameters ['name']” 实现ParameterAware接口
HttpServletSession类 无(需通过HttpServletRequest来获得) 一次Http Session会话 无方便的方法 无法注入
session的Map ActionContext. get("session") 每次请求创建,但在一次Http Session会话中数据都是一样的 使用“#session”再加上key,如“# session.name”或者“#session ['name']” 实现SessionAware接口
ServletContext类 ActionContext. get( StrutsStatics. SERVLET_CONTEXT) 网站项目启动后一直存在且唯一 无方便的方法 使用ServletContextAware接口
application的Map ActionContext.get( "application") 每次请求时创建,但其中的数据是网站项目启动后一直存在且共享 使用“# application”再加上key,如“#application .name”或者“#application ['name']” 使用ApplicationAware接口

附录1 ActionContext中到底有哪些数据

key key的声明处 value的类型 value.toString()

com. opensymphony. xwork2. dispatcher.

HttpServletRequest

StrutsStatics. HTTP_REQUEST org. apache. struts2. dispatcher. StrutsRequestWrapper org. apache. struts2. dispatcher. StrutsRequestWrapper @10984e0
application org. apache. struts2. dispatcher. ApplicationMap
com. opensymphony. xwork2. ActionContext. locale ActionContext. LOCALE java. util. Locale zh_CN
com. opensymphony. xwork2. dispatcher. HttpServletResponse StrutsStatics. HTTP_RESPONSE org. apache. catalina. connector. ResponseFacade org. apache. catalina. connector. ResponseFacade @14ecfe8

xwork. NullHandler.

createNullObjects

Boolean false
com. opensymphony. xwork2. ActionContext. name ActionContext. ACTION_NAME String index

com.opensymphony. xwork2.ActionContext.

conversionErrors

ActionContext.

CONVERSION_ERRORS

java. util. HashMap {}
com. opensymphony. xwork2. ActionContext. application ActionContext. APPLICATION org. apache. struts2. dispatcher. ApplicationMap
attr org. apache. struts2. util. AttributeMap org. apache. struts2. util. AttributeMap @133a2a8
com. opensymphony. xwork2. ActionContext. container ActionContext. CONTAINER com. opensymphony. xwork2. inject. ContainerImpl com. opensymphony. xwork2. inject. ContainerImpl @fc02c8
com. opensymphony. xwork2. dispatcher. ServletContext StrutsStatics. SERVLET_CONTEXT org. apache. catalina. core. ApplicationContextFacade org. apache. catalina. core. ApplicationContextFacade @11ad78c
com. opensymphony. xwork2. ActionContext. session ActionContext. SESSION org.apache.struts2. dispatcher.SessionMap {}

com.opensymphony. xwork2.ActionContext.

actionInvocation

ActionContext. ACTION_INVOCATION com. opensymphony. xwork2. DefaultActionInvocation com. opensymphony. xwork2. DefaultActionInvocation @13d4497
xwork. MethodAccessor. denyMethodExecution 笔者很懒,没有找 Boolean false
report. conversion. errors 笔者很懒,没有找 Boolean false
session org. apache. struts2. dispatcher. SessionMap {}
com. opensymphony. xwork2. util. ValueStack. ValueStack ValueStack.VALUE_STACK com. opensymphony. xwork2. ognl. OgnlValueStack com. opensymphony. xwork2. ognl. OgnlValueStack @16237fd
request org. apache. struts2. dispatcher. RequestMap
action 笔者很懒,没有找 com. example. MyAction
struts. actionMapping 笔者很懒,没有找 org. apache. struts2. dispatcher. mapper. ActionMapping org. apache. struts2. dispatcher. mapper. ActionMapping @892cc5
parameters java. util. HashMap {}
com. opensymphony. xwork2. ActionContext. parameters ActionContext.PARAMETERS java. util. TreeMap {}

注意:该表格为了排版在某些地方加了空格。

可以看出,有些相同的对象被以不同的key多次设置到ActionContext中。如果想看看创建ActionContext的源代码,请看org.apache.struts2.dispatcher.Dispatcher的serviceAction方法和两个createContextMap方法。


附录2 Struts2标签中value属性直接对ActionContext访问的问题

经试验并查看相关源代码后发现,在使用<s:property value="..."/>时,该标签的执行类会先根据value中表达式到值栈中执行表达式来查找值。如果在值栈中找到值,就返回该值;如果没有找到,则把该表达式作为ActionContext的key,到ActionContext中去找值。比如<s:property value="request"/>也会得到ActionContext中的request,等价于<s:property value="#request"/>。但是,由于标签的执行类会认为该值时String类型的,并且会直接进行类型转换。于是,如果直接使用&lt;s:property value="request"/>的话其实会让页面抛出异常:Request类型不能转换成String类型。所以,只能用如果不带#的话只能成功读取ActionContext中String类型的值。这种机制使得某些时候栈顶的属性可以覆盖ActionContext中的key,或许你正需要它。然而,鉴于这种机制的不确定性,笔者建议访问ActionContext中的数据一定要带上“#”,可以免去一些麻烦。

关于这种转型异常,笔者认为是Struts2的bug,源代码如下,当“value = getValue(expr, asType);”时,是考虑了asType的,但从context中读取时“value = findInContext(expr);”,就没有考虑asType,并且没有在其他地方看到类型检查操作:

Java代码复制代码收藏代码
  1. //本代码截取Struts2.3.1.2版本com.opensymphony.xwork2.ognl.OgnlValueStack类的第340行-352行
  2. privateObjecttryFindValue(Stringexpr,ClassasType)throwsOgnlException{
  3. Objectvalue=null;
  4. try{
  5. expr=lookupForOverrides(expr);
  6. value=getValue(expr,asType);
  7. if(value==null){
  8. value=findInContext(expr);
  9. }
  10. }finally{
  11. context.remove(THROW_EXCEPTION_ON_FAILURE);
  12. }
  13. returnvalue;
  14. }
分享到:
评论

相关推荐

    Struts2中关于ValueStack的一些操作

    我们在开发中,几乎不需要知道OGNL的细节。 4、 WebWork为每一次请求构建一个ValueStack,并将所有相关的数据对象(例如:Action对象、Model对象等)放到ValueStack中。再将ValueStack暴露给视图页面,这样页面就...

    Struts中的ognl和valueStack

    深入讲解Struts中的ognl和valueStack

    Struts2中的参数传递

    我们知道,Struts2完成参数传递处理工作的基础是OGNL和ValueStack。而在这个 过程中,我也把Struts2所要做的工作大致归纳为两个方面: 1. 对OGNL操作进行封装,完成OGNL表达式所表示的值到Java对象的值传递机制 2. ...

    Struts2 in action中文版

    9.1 为什么在Struts 2中使用Spring 196 9.1.1 依赖注入能做些什么 197 9.1.2 Spring如何管理对象和注入依赖 199 9.1.3 使用接口隐藏实现 200 9.2 将Spring添加到Struts 2 202 9.2.1 让Spring管理动作、拦截器和结果...

    马士兵Struts2笔记2013

    建立一个Struts2 工程,用Action的属性接收参数,使用Domain Model (实体模型) 接收参数,Struts2_2.1.6版本的中文问题,Struts模块包含,Struts简单数据验证 ,Struts ValueStack(值栈) Debug,Value Stack ...

    linjin101#javaStudy#Struts2中的OGNL和值栈ValueStack1

    1、什么是值栈 2、值栈的内部结构 3、ActionContext和ValueStatck的关系 4、如何获取值栈对象 5、向值栈存数据 6、从值栈中获取数据

    struts学习笔记(3)

    当客户端向action发送请求并且最后跳转到另外一个页面的时候,在跳转的同时,struts2框架会帮我们自动把需要传到页面的值放这两个对象当中去,然后我们在页面就可以使用固定的方式把值从这个两个对象里面取出来了。...

    Struts2入门教程(全新完整版)

    十二、总结 本教程对struts2的基本知识进行了一些说明,关于struts2的更多详细内容应参看struts2的官方文档及提供的app实例。 下面对struts2的基本执行流程作一简要说明,此流程说明可以结合官方提供的struts2结构图...

    struts2 学习例子

    Struts2的标签库有一个巨大的改进之处,struts2标签库的标签不依赖于任何表现层技术,也就是说strtus2提供了大部分标签,可以在各种表现技术中使用。包括最常用的jsp页面,也可以说Velocity和FreeMarker等模板技术...

    Struts用的ognl和valueStack(vs)实例

    Struts用的ognl和valueStack(vs)实例

    Struts2 Value Stack Contents 中取值、多个集合数组

    Struts2 Value Stack Contents 中取值、多个集合数组示例

    struts2 标签库使用文档

    Struts2标签库的组成 ...数据访问标签:主要包含用于输出值栈(ValueStack)中的值,完成国际化等功能的标签。 流程控制标签:主要包含用于实现分支,循环等流程控制的标签。 AJAX标签:用于支持Ajax效果

    Struts2练习Demo以及随笔

    Struts的开发步骤、OGNL、ValueStack、Action核心、Result基本原理、Struts2核心标记库、Struts2拦截器、自定义拦截器、UI标记、非UI标记、资源文件国际化等等实例全面使用。

    OGNL表达式语言.txt

    OGNL表达式语言: 相对EL表达式,它提供了平时我们需要的一些功能,如: ...另外OGNL会设定一个根对象(root对象),在Struts2中根对象是ValueStack。 如果访问根对象中的对象的属性,则可以省略#命名空间。

    在线培训:ValueStack

    培训主要学习以下几个知识点: 1、制作PPT,讲解栈的特点和使用方法 2、编码实现回文对 3、制作PPT,讲解Struts2中的ValueStack 4、编码改变ValueStack中的对象的顺序

    Struts2基础教程

    讲解Struts2入门基础,包括Action、Result、ValueStack等,Struts2初学者会有帮助

    Struts2框架基础 二

    OGNL表达式 interceptor拦截器 valuestack的存入 /取出

    struts2的值栈

    用一个小小的flash来形象的演示struts2中值栈的操作过程,非常直观!

    精讲Struts2

    精简全面讲解Struts 2.0Struts2产生背景 工作原理 实现步骤 标签 命名空间 转换器 国际化 输出 令牌机制 拦截器 表单校验(jQuery.validate验证框架) 上传 下载 OGNL(ActionContext StackContext ValueStack区别)

Global site tag (gtag.js) - Google Analytics