在Struts2中使用ValueStack、ActionContext、ServletContext、request、session?在Struts2中使用ValueStack、Ac
在Struts2中使用ValueStack、ActionContext、ServletContext、request、session
?
在Struts2中使用ValueStack、ActionContext、ServletContext、request、session等 .??
目录(?)[+]
?Java代码?
?
- //?此类为一个封装数据的简单类,在下面的例子会用到???
- public?class?Person?{ ??
- ??
- ????private?String?name; ??
- ??
- ????public?String?getName()?{ ??
- ????????return?name; ??
- ????} ??
- ??
- ????public?void?setName(String?name)?{ ??
- ????????this.name?=?name; ??
- ????} ??
- }??
[java]?view plaincopy?
- //?此类为一个封装数据的简单类,在下面的例子会用到??
- public?class?Person?{??
- ??
- ????private?String?name;??
- ??
- ????public?String?getName()?{??
- ????????return?name;??
- ????}??
- ??
- ????public?void?setName(String?name)?{??
- ????????this.name?=?name;??
- ????}??
- }??
??Java代码?
?
- //?本类将演示拦截器中对值栈的操作???
- public?class?MyInterceptor?extends?AbstractInterceptor?{ ??
- ??
- ????public?String?intercept(ActionInvocation?invocation)?throws?Exception?{ ??
- ????????//?获得值栈???
- ????????ValueStack?valueStack?=?invocation.getStack(); ??
- ????????//?存入值???
- ????????Person?person?=?new?Person(); ??
- ????????valueStack.push(person); ??
- ????????//?执行表达式获取值???
- ????????String?name?=?(String)?valueStack.findValue("name"); ??
- ????????//?其他代码???
- ????????return?invocation.invoke(); ??
- ????} ??
- }??
[java]?view plaincopy?
- //?本类将演示拦截器中对值栈的操作??
- public?class?MyInterceptor?extends?AbstractInterceptor?{??
- ??
- ????public?String?intercept(ActionInvocation?invocation)?throws?Exception?{??
- ????????//?获得值栈??
- ????????ValueStack?valueStack?=?invocation.getStack();??
- ????????//?存入值??
- ????????Person?person?=?new?Person();??
- ????????valueStack.push(person);??
- ????????//?执行表达式获取值??
- ????????String?name?=?(String)?valueStack.findValue("name");??
- ????????//?其他代码??
- ????????return?invocation.invoke();??
- ????}??
- }??
?Java代码?
?
- //?本类将演示在Action中对值栈进行操作???
- public?class?MyAction?extends?ActionSupport?{ ??
- ??
- ????@Override??
- ????public?String?execute()?throws?Exception?{ ??
- ????????//?获得值栈???
- ????????ValueStack?valueStack?=?ActionContext.getContext().getValueStack(); ??
- ????????//?存入值???
- ????????Person?person?=?new?Person();//?这是之前例子中定义的类??
- ????????valueStack.push(person); ??
- ????????//?执行表达式获取值???
- ????????String?name?=?(String)?valueStack.findValue("name"); ??
- ????????//?其他代码???
- ????????//?......???
- ????????return?SUCCESS; ??
- ????} ??
- ????//?以下定义的属性供接下来的JSP例子使用???
- ????private?String?message; ??
- ????private?Person?person; ??
- ????private?List<Person>?personList; ??
- ??
- ????public?String?getMessage()?{ ??
- ????????return?message; ??
- ????} ??
- ??
- ????public?Person?getPerson()?{ ??
- ????????return?person; ??
- ????} ??
- ??
- ????public?List<Person>?getPersonList()?{ ??
- ????????return?personList; ??
- ????} ??
- }??
[java]?view plaincopy?
- //?本类将演示在Action中对值栈进行操作??
- public?class?MyAction?extends?ActionSupport?{??
- ??
- ????@Override??
- ????public?String?execute()?throws?Exception?{??
- ????????//?获得值栈??
- ????????ValueStack?valueStack?=?ActionContext.getContext().getValueStack();??
- ????????//?存入值??
- ????????Person?person?=?new?Person();//?这是之前例子中定义的类??
- ????????valueStack.push(person);??
- ????????//?执行表达式获取值??
- ????????String?name?=?(String)?valueStack.findValue("name");??
- ????????//?其他代码??
- ????????//?......??
- ????????return?SUCCESS;??
- ????}??
- ????//?以下定义的属性供接下来的JSP例子使用??
- ????private?String?message;??
- ????private?Person?person;??
- ????private?List<Person>?personList;??
- ??
- ????public?String?getMessage()?{??
- ????????return?message;??
- ????}??
- ??
- ????public?Person?getPerson()?{??
- ????????return?person;??
- ????}??
- ??
- ????public?List<Person>?getPersonList()?{??
- ????????return?personList;??
- ????}??
- }??
???Html代码?
?
- <%@page?contentType="text/html"?pageEncoding="UTF-8"%>??
- <%@taglib?uri="/struts-tags"?prefix="s"?%>??
- <!DOCTYPE?html>??
- <html>??
- ????<head>??
- ????????<meta?http-equiv="Content-Type"?content="text/html;?charset=UTF-8">??
- ????????<title>JSP?Page</title>??
- ????</head>??
- ????<body>??
- ????????<!--?本JSP将演示在JSP中对值栈的使用?-->??
- ????????<!--?本JSP为MyAction对应的JSP?-->??
- ??
- ????????<!--?由于Action已经被存入的值栈,所以可以调用Action的属性??-->??
- ????????<!--?使用下面的标签和表达式来显示MyAction的message属性?-->??
- ????????<s:property?value="message"/>??
- ????????<!--?使用下面的标签和表达式来调用Action的getText(...)方法,参数为MyAction的message属性??-->??
- ????????<s:property?value="getText(message)"/>??
- ????????<!--?默认情况下传递给cssClass的是字符串常量。可以使用“%{}”来启用Ognl,这样,传递给cssClass的就不是字符串常量"message",而是上面所说的message的值??-->??
- ????????<s:div?cssClass="%{message}"/>??
- ????????<!--?使用s:push标签来将对象放入值栈,如下?-->??
- ????????<s:push?value="person">??
- ????????????<!--?在此s:push标签内,值栈的栈顶元素为person,栈顶第二层为action ??
- ????????????<!--?在标签内直接调用person的属性(而不是Action的属性),如下?-->??
- ????????????<s:property?value="name"/>??
- ????????????<!--?在标签内也可以使用MyAction的属性,值栈会依次先查找Person是否有该属性,由于没找到,会再MyAction中再查找,如下??-->??
- ????????????<s:property?value="message"/>??
- ????????????<!--?可以使用“[0]”、“[1]”等指定从值栈的哪一层开始查找??-->??
- ????????????<!--?此时,使用“[0]”表示从Person开始查找,当然还是找不到,值栈就接着到MyAction中查找,如下??-->??
- ????????????<s:property?value="[0].message"/>??
- ????????????<!--?此时,使用“[1]”将从MyAction开始查找,而跳过了person,如下??-->??
- ????????????<s:property?value="[1].message"/>??
- ????????????<!--?想要访问栈顶元素本身使用关键字“top”,比如,下面的top就代表栈顶的person,如下?-->??
- ????????????<s:property?value="top"/>??
- ????????????<!--?或者如下??-->??
- ????????????<s:property?value="[0].top"/>??
- ????????????<!--?想要访问MyAction本身的话使用如下写法??-->??
- ????????????<s:property?value="[1].top"/>??
- ????????</s:push>??
- ????????<!--?此时person已被移出值栈,再使用如下标签和表达式将无法得到值?-->??
- ????????<!--<s:property?value="name"/>-->??
- ????????<!--?iterator标签会把list的每个元素依次存入栈顶,如下?-->??
- ????????<s:iterator?value="personList">??
- ????????????<!--?获得List每个元素中的name属性??-->??
- ????????????<s:property?value="name"/>??
- ????????</s:iterator>??
- ????</body>??
- </html>??
[html]?view plaincopy?
- <%@page?contentType="text/html"?pageEncoding="UTF-8"%>??
- <%@taglib?uri="/struts-tags"?prefix="s"?%>??
- <!DOCTYPE?html>??
- <html>??
- ????<head>??
- ????????<meta?http-equiv="Content-Type"?content="text/html;?charset=UTF-8">??
- ????????<title>JSP?Page</title>??
- ????</head>??
- ????<body>??
- ????????<!--?本JSP将演示在JSP中对值栈的使用?-->??
- ????????<!--?本JSP为MyAction对应的JSP?-->??
- ??
- ????????<!--?由于Action已经被存入的值栈,所以可以调用Action的属性??-->??
- ????????<!--?使用下面的标签和表达式来显示MyAction的message属性?-->??
- ????????<s:property?value="message"/>??
- ????????<!--?使用下面的标签和表达式来调用Action的getText(...)方法,参数为MyAction的message属性??-->??
- ????????<s:property?value="getText(message)"/>??
- ????????<!--?默认情况下传递给cssClass的是字符串常量。可以使用“%{}”来启用Ognl,这样,传递给cssClass的就不是字符串常量"message",而是上面所说的message的值??-->??
- ????????<s:div?cssClass="%{message}"/>??
- ????????<!--?使用s:push标签来将对象放入值栈,如下?-->??
- ????????<s:push?value="person">??
- ????????????<!--?在此s:push标签内,值栈的栈顶元素为person,栈顶第二层为action??
- ????????????<!--?在标签内直接调用person的属性(而不是Action的属性),如下?-->??
- ????????????<s:property?value="name"/>??
- ????????????<!--?在标签内也可以使用MyAction的属性,值栈会依次先查找Person是否有该属性,由于没找到,会再MyAction中再查找,如下??-->??
- ????????????<s:property?value="message"/>??
- ????????????<!--?可以使用“[0]”、“[1]”等指定从值栈的哪一层开始查找??-->??
- ????????????<!--?此时,使用“[0]”表示从Person开始查找,当然还是找不到,值栈就接着到MyAction中查找,如下??-->??
- ????????????<s:property?value="[0].message"/>??
- ????????????<!--?此时,使用“[1]”将从MyAction开始查找,而跳过了person,如下??-->??
- ????????????<s:property?value="[1].message"/>??
- ????????????<!--?想要访问栈顶元素本身使用关键字“top”,比如,下面的top就代表栈顶的person,如下?-->??
- ????????????<s:property?value="top"/>??
- ????????????<!--?或者如下??-->??
- ????????????<s:property?value="[0].top"/>??
- ????????????<!--?想要访问MyAction本身的话使用如下写法??-->??
- ????????????<s:property?value="[1].top"/>??
- ????????</s:push>??
- ????????<!--?此时person已被移出值栈,再使用如下标签和表达式将无法得到值?-->??
- ????????<!--<s:property?value="name"/>-->??
- ????????<!--?iterator标签会把list的每个元素依次存入栈顶,如下?-->??
- ????????<s:iterator?value="personList">??
- ????????????<!--?获得List每个元素中的name属性??-->??
- ????????????<s:property?value="name"/>??
- ????????</s:iterator>??
- ????</body>??
- </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)。?? ? 一些例子:Java代码?
?
- //?本类将演示拦截器中对ActionContext的操作???
- public?class?MyInterceptor?extends?AbstractInterceptor?{ ??
- ??
- ????public?String?intercept(ActionInvocation?invocation)?throws?Exception?{ ??
- ????????//?获得ActionContext???
- ????????ActionContext?actionContext?=?invocation.getInvocationContext(); ??
- ????????//?存入值???
- ????????Person?person?=?new?Person(); ??
- ????????actionContext.put("person",?person); ??
- ????????//?获取值???
- ????????Object?value?=?actionContext.get("person"); ??
- ????????//?获取HttpServletRequest???
- ????????HttpServletRequest?request?=?(HttpServletRequest)?actionContext.get(StrutsStatics.HTTP_REQUEST); ??
- ????????//?获取request的Map,即HttpServletRequest.getAttribute(...)和HttpServletRequest.setAttribute(...)所操作的值??
- ????????Map?requestMap?=?(Map)?actionContext.get("request"); ??
- ????????//?其他代码???
- ????????//?......???
- ????????return?invocation.invoke(); ??
- ????} ??
- }??
[java]?view plaincopy?
- //?本类将演示拦截器中对ActionContext的操作??
- public?class?MyInterceptor?extends?AbstractInterceptor?{??
- ??
- ????public?String?intercept(ActionInvocation?invocation)?throws?Exception?{??
- ????????//?获得ActionContext??
- ????????ActionContext?actionContext?=?invocation.getInvocationContext();??
- ????????//?存入值??
- ????????Person?person?=?new?Person();??
- ????????actionContext.put("person",?person);??
- ????????//?获取值??
- ????????Object?value?=?actionContext.get("person");??
- ????????//?获取HttpServletRequest??
- ????????HttpServletRequest?request?=?(HttpServletRequest)?actionContext.get(StrutsStatics.HTTP_REQUEST);??
- ????????//?获取request的Map,即HttpServletRequest.getAttribute(...)和HttpServletRequest.setAttribute(...)所操作的值??
- ????????Map?requestMap?=?(Map)?actionContext.get("request");??
- ????????//?其他代码??
- ????????//?......??
- ????????return?invocation.invoke();??
- ????}??
- }??
?Java代码?
?
- //?本类将演示在Action中对ActionContext进行操作???
- public?class?MyAction?extends?ActionSupport?{ ??
- ??
- ????@Override??
- ????public?String?execute()?throws?Exception?{ ??
- ????????//?获得值栈???
- ????????ActionContext?actionContext?=?ActionContext.getContext(); ??
- ????????//?存入值???
- ????????Person?person?=?new?Person();//?这是之前例子中定义的类??
- ????????actionContext.put("person",?person); ??
- ????????//?获取值???
- ????????Object?object?=?actionContext.get("person"); ??
- ????????//?其他代码???
- ????????//?......???
- ????????return?SUCCESS; ??
- ????} ??
- }??
[java]?view plaincopy?
- //?本类将演示在Action中对ActionContext进行操作??
- public?class?MyAction?extends?ActionSupport?{??
- ??
- ????@Override??
- ????public?String?execute()?throws?Exception?{??
- ????????//?获得值栈??
- ????????ActionContext?actionContext?=?ActionContext.getContext();??
- ????????//?存入值??
- ????????Person?person?=?new?Person();//?这是之前例子中定义的类??
- ????????actionContext.put("person",?person);??
- ????????//?获取值??
- ????????Object?object?=?actionContext.get("person");??
- ????????//?其他代码??
- ????????//?......??
- ????????return?SUCCESS;??
- ????}??
- }??
?Html代码?
?
- <!DOCTYPE?html>??
- <html>??
- ????<head>??
- ????????<meta?http-equiv="Content-Type"?content="text/html;?charset=UTF-8">??
- ????????<title>JSP?Page</title>??
- ????</head>??
- ????<body>??
- ????????<!--?本JSP将演示在JSP中对ActionContext的使用?-->??
- ????????<!--?本JSP为MyAction对应的JSP?-->??
- ??
- ????????<!--?由于Action中已经向ActionContext存入了key为"person"的值,所以可以使用“#person”来获取它,如下??-->??
- ????????<s:property?value="#person"/>??
- ????????<!--?获得person的name属性,如下??-->??
- ????????<s:property?value="#person.name"/>??
- ????????<!--?获得Struts2在ActionContext中存入的值,比如request的Map,如下??-->??
- ????????<s:property?value="#request"/>??
- ????????<!--?获得Struts2在ActionContext中存入的值,比如session的Map,如下??-->??
- ????????<s:property?value="#session"/>??
- ????????<!--?获得Struts2在ActionContext中存入的值,request请求传递的GET参数或POST参数的Map,如下??-->??
- ????????<s:property?value="#parameters"/>??
- ???????? ??
- ????????<!--?以下演示在JSP中把值存入ActionContext中??-->??
- ????????<!--?存入一个字符串"myName",key为"myKey",如下?-->??
- ????????<s:set?value="%{'myName'}"?var="myKey"/>??
- ????????<!--?使用s:bean标签来创建一个对象,并把它存入ActionContext中,key为myObject,如下??-->??
- ????????<s:bean?name="com.example.Person"?var="myObject"/>??
- ????????<!--?之后就可以用“#”来读取它们,如下??-->??
- ????????<s:property?value="#myKey"/>??
- ????????<s:property?value="#myObject"/>??
- ????</body>??
- </html>??
[html]?view plaincopy?
- <!DOCTYPE?html>??
- <html>??
- ????<head>??
- ????????<meta?http-equiv="Content-Type"?content="text/html;?charset=UTF-8">??
- ????????<title>JSP?Page</title>??
- ????</head>??
- ????<body>??
- ????????<!--?本JSP将演示在JSP中对ActionContext的使用?-->??
- ????????<!--?本JSP为MyAction对应的JSP?-->??
- ??
- ????????<!--?由于Action中已经向ActionContext存入了key为"person"的值,所以可以使用“#person”来获取它,如下??-->??
- ????????<s:property?value="#person"/>??
- ????????<!--?获得person的name属性,如下??-->??
- ????????<s:property?value="#person.name"/>??
- ????????<!--?获得Struts2在ActionContext中存入的值,比如request的Map,如下??-->??
- ????????<s:property?value="#request"/>??
- ????????<!--?获得Struts2在ActionContext中存入的值,比如session的Map,如下??-->??
- ????????<s:property?value="#session"/>??
- ????????<!--?获得Struts2在ActionContext中存入的值,request请求传递的GET参数或POST参数的Map,如下??-->??
- ????????<s:property?value="#parameters"/>??
- ??????????
- ????????<!--?以下演示在JSP中把值存入ActionContext中??-->??
- ????????<!--?存入一个字符串"myName",key为"myKey",如下?-->??
- ????????<s:set?value="%{'myName'}"?var="myKey"/>??
- ????????<!--?使用s:bean标签来创建一个对象,并把它存入ActionContext中,key为myObject,如下??-->??
- ????????<s:bean?name="com.example.Person"?var="myObject"/>??
- ????????<!--?之后就可以用“#”来读取它们,如下??-->??
- ????????<s:property?value="#myKey"/>??
- ????????<s:property?value="#myObject"/>??
- ????</body>??
- </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代码?
?
- //?本类将演示拦截器中对HttpServletRequest和request的Map的操作???
- public?class?MyInterceptor?extends?AbstractInterceptor?{ ??
- ??
- ????public?String?intercept(ActionInvocation?invocation)?throws?Exception?{ ??
- ????????//?获得ActionContext???
- ????????ActionContext?actionContext?=?invocation.getInvocationContext(); ??
- ????????//?获得HttpServletRequest???
- ????????HttpServletRequest?httpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST); ??
- ????????//?获得request的Map???
- ????????Map?requestMap?=?(Map)?actionContext.get("request"); ??
- ????????//?创建一个类作为实例???
- ????????Person?person?=?new?Person(); ??
- ????????//?以下两行的语句作用相同???
- ????????httpServletRequest.setAttribute("person",?person); ??
- ????????requestMap.put("person",?person); ??
- ????????//?其他代码???
- ????????//?......???
- ????????return?invocation.invoke(); ??
- ????} ??
- }??
[java]?view plaincopy?
- //?本类将演示拦截器中对HttpServletRequest和request的Map的操作??
- public?class?MyInterceptor?extends?AbstractInterceptor?{??
- ??
- ????public?String?intercept(ActionInvocation?invocation)?throws?Exception?{??
- ????????//?获得ActionContext??
- ????????ActionContext?actionContext?=?invocation.getInvocationContext();??
- ????????//?获得HttpServletRequest??
- ????????HttpServletRequest?httpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST);??
- ????????//?获得request的Map??
- ????????Map?requestMap?=?(Map)?actionContext.get("request");??
- ????????//?创建一个类作为实例??
- ????????Person?person?=?new?Person();??
- ????????//?以下两行的语句作用相同??
- ????????httpServletRequest.setAttribute("person",?person);??
- ????????requestMap.put("person",?person);??
- ????????//?其他代码??
- ????????//?......??
- ????????return?invocation.invoke();??
- ????}??
- }??
?
?
?
Java代码?
?
- //?本类将演示在Action中对HttpServletRequest和request的Map进行操作(静态方法获得ActionContext)??
- public?class?MyAction?extends?ActionSupport?{ ??
- ??
- ????@Override??
- ????public?String?execute()?throws?Exception?{ ??
- ????????//?获得ActionContext???
- ????????ActionContext?actionContext?=?ActionContext.getContext(); ??
- ????????//?获得HttpServletRequest???
- ????????HttpServletRequest?httpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST); ??
- ????????//?获得request的Map???
- ????????Map?requestMap?=?(Map)?actionContext.get("request"); ??
- ????????//?创建一个类作为实例???
- ????????Person?person?=?new?Person(); ??
- ????????//?以下两行的语句作用相同???
- ????????httpServletRequest.setAttribute("person",?person); ??
- ????????requestMap.put("person",?person); ??
- ????????//?其他代码???
- ????????//?......???
- ????????return?SUCCESS; ??
- ????} ??
- }??
[java]?view plaincopy?
- //?本类将演示在Action中对HttpServletRequest和request的Map进行操作(静态方法获得ActionContext)??
- public?class?MyAction?extends?ActionSupport?{??
- ??
- ????@Override??
- ????public?String?execute()?throws?Exception?{??
- ????????//?获得ActionContext??
- ????????ActionContext?actionContext?=?ActionContext.getContext();??
- ????????//?获得HttpServletRequest??
- ????????HttpServletRequest?httpServletRequest=(HttpServletRequest)actionContext.get(StrutsStatics.HTTP_REQUEST);??
- ????????//?获得request的Map??
- ????????Map?requestMap?=?(Map)?actionContext.get("request");??
- ????????//?创建一个类作为实例??
- ????????Person?person?=?new?Person();??
- ????????//?以下两行的语句作用相同??
- ????????httpServletRequest.setAttribute("person",?person);??
- ????????requestMap.put("person",?person);??
- ????????//?其他代码??
- ????????//?......??
- ????????return?SUCCESS;??
- ????}??
- }??
?
?
?
Java代码?
?
- //?本类将演示在Action中使用ServletRequestAware获得HttpServletRequest(注意:要使用ServletConfigInterceptor拦截器)??
- public?class?MyAction?extends?ActionSupport?implements?ServletRequestAware?{ ??
- ??
- ????private?HttpServletRequest?request; ??
- ???? ??
- ????//此方法是接口ServletRequestAware的方法???
- ????public?void?setServletRequest(HttpServletRequest?request)?{ ??
- ????????this.request?=?request; ??
- ????} ??
- ??
- ????@Override??
- ????public?String?execute()?throws?Exception?{ ??
- ????????//?HttpServletRequest已在该类的字段中准备好,可直接使用??
- ????????//?......???
- ????????return?SUCCESS; ??
- ????} ??
- }??
[java]?view plaincopy?
- //?本类将演示在Action中使用ServletRequestAware获得HttpServletRequest(注意:要使用ServletConfigInterceptor拦截器)??
- public?class?MyAction?extends?ActionSupport?implements?ServletRequestAware?{??
- ??
- ????private?HttpServletRequest?request;??
- ??????
- ????//此方法是接口ServletRequestAware的方法??
- ????public?void?setServletRequest(HttpServletRequest?request)?{??
- ????????this.request?=?request;??
- ????}??
- ??
- ????@Override??
- ????public?String?execute()?throws?Exception?{??
- ????????//?HttpServletRequest已在该类的字段中准备好,可直接使用??
- ????????//?......??
- ????????return?SUCCESS;??
- ????}??
- }??
?
?
Java代码?
?
- //?本类将演示在Action中使用ServletRequestAware获得request的Map(注意:要使用ServletConfigInterceptor拦截器)??
- public?class?MyAction?extends?ActionSupport?implements?RequestAware?{ ??
- ??
- ????Map<String,?Object>?request; ??
- ??
- ????//?该方法是接口RequestAware的方法???
- ????public?void?setRequest(Map<String,?Object>?request)?{ ??
- ????????this.request?=?request; ??
- ????} ??
- ??
- ????@Override??
- ????public?String?execute()?throws?Exception?{ ??
- ????????//?request的Map已在该类的字段中准备好,可直接使用???
- ????????//?......???
- ????????return?SUCCESS; ??
- ????} ??
- }??
[java]?view plaincopy?
- //?本类将演示在Action中使用ServletRequestAware获得request的Map(注意:要使用ServletConfigInterceptor拦截器)??
- public?class?MyAction?extends?ActionSupport?implements?RequestAware?{??
- ??
- ????Map<String,?Object>?request;??
- ??
- ????//?该方法是接口RequestAware的方法??
- ????public?void?setRequest(Map<String,?Object>?request)?{??
- ????????this.request?=?request;??
- ????}??
- ??
- ????@Override??
- ????public?String?execute()?throws?Exception?{??
- ????????//?request的Map已在该类的字段中准备好,可直接使用??
- ????????//?......??
- ????????return?SUCCESS;??
- ????}??
- }??
?
?
Java代码?
?
- <!DOCTYPE?html> ??
- <html> ??
- ????<head> ??
- ????????<meta?http-equiv="Content-Type"?content="text/html;?charset=UTF-8"> ??
- ????????<title>JSP?Page</title> ??
- ????</head> ??
- ????<body> ??
- ????????<!--?本JSP将演示在JSP中对request的Map的使用?--> ??
- ????????<!--?本JSP为MyAction对应的JSP?--> ??
- ??
- ????????<!--?request的Map是Struts2自动在ActionContext中存入的值(key为request),所以使用“#”来访问ActionContext,从中读取request??--> ??
- ????????<s:property?value="#request"/> ??
- ????????<!--?以下两行均是访问request的Map中key为“name”的值??--> ??
- ????????<s:property?value="#request.name"/> ??
- ????????<s:property?value="#request['name']"/> ??
- ????</body> ??
- </html>??
[java]?view plaincopy?
- <!DOCTYPE?html>??
- <html>??
- ????<head>??
- ????????<meta?http-equiv="Content-Type"?content="text/html;?charset=UTF-8">??
- ????????<title>JSP?Page</title>??
- ????</head>??
- ????<body>??
- ????????<!--?本JSP将演示在JSP中对request的Map的使用?-->??
- ????????<!--?本JSP为MyAction对应的JSP?-->??
- ??
- ????????<!--?request的Map是Struts2自动在ActionContext中存入的值(key为request),所以使用“#”来访问ActionContext,从中读取request??-->??
- ????????<s:property?value="#request"/>??
- ????????<!--?以下两行均是访问request的Map中key为“name”的值??-->??
- ????????<s:property?value="#request.name"/>??
- ????????<s:property?value="#request['name']"/>??
- ????</body>??
- </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的MapActionContext. get("request")一次Http请求使用“#request”再加上key,如“#request.name”或者“#request['name']”实现RequestAware接口parameters的MapActionContext. get( "parameters")一次Http请求使用“# parameters”再加上key,如“#parameters .name”或者“#parameters ['name']”实现ParameterAware接口HttpServletSession类无(需通过HttpServletRequest来获得)一次Http Session会话无方便的方法无法注入session的MapActionContext. get("session")每次请求创建,但在一次Http Session会话中数据都是一样的使用“#session”再加上key,如“# session.name”或者“#session ['name']”实现SessionAware接口ServletContext类ActionContext. get( StrutsStatics. SERVLET_CONTEXT)网站项目启动后一直存在且唯一无方便的方法使用ServletContextAware接口application的MapActionContext.get( "application")每次请求时创建,但其中的数据是网站项目启动后一直存在且共享使用“# application”再加上key,如“#application .name”或者“#application ['name']”使用ApplicationAware接口
?
附录1 ActionContext中到底有哪些数据
?
keykey的声明处value的类型value.toString()
com. opensymphony. xwork2. dispatcher.
HttpServletRequest
?StrutsStatics. HTTP_REQUESTorg. apache. struts2. dispatcher. StrutsRequestWrapperorg. apache. struts2. dispatcher. StrutsRequestWrapper @10984e0application?无org. apache. struts2. dispatcher. ApplicationMap略com. opensymphony. xwork2. ActionContext. locale?ActionContext. LOCALEjava. util. Localezh_CNcom. opensymphony. xwork2. dispatcher. HttpServletResponse?StrutsStatics. HTTP_RESPONSEorg. apache. catalina. connector. ResponseFacadeorg. apache. catalina. connector. ResponseFacade @14ecfe8
xwork. NullHandler.
createNullObjects
?Booleanfalsecom. opensymphony. xwork2. ActionContext. name?ActionContext. ACTION_NAMEStringindex
com.opensymphony. xwork2.ActionContext.
conversionErrors
?ActionContext.?
CONVERSION_ERRORS
java. util. HashMap{}com. opensymphony. xwork2. ActionContext. application?ActionContext. APPLICATIONorg. apache. struts2. dispatcher. ApplicationMap略attr?无org. apache. struts2. util. AttributeMaporg. apache. struts2. util. AttributeMap @133a2a8com. opensymphony. xwork2. ActionContext. container?ActionContext. CONTAINERcom. opensymphony. xwork2. inject. ContainerImplcom. opensymphony. xwork2. inject. ContainerImpl @fc02c8com. opensymphony. xwork2. dispatcher. ServletContext?StrutsStatics. SERVLET_CONTEXTorg. apache. catalina. core. ApplicationContextFacadeorg. apache. catalina. core. ApplicationContextFacade @11ad78ccom. opensymphony. xwork2. ActionContext. session?ActionContext. SESSIONorg.apache.struts2. dispatcher.SessionMap{}
com.opensymphony. xwork2.ActionContext.
actionInvocation
?ActionContext. ACTION_INVOCATIONcom. opensymphony. xwork2. DefaultActionInvocationcom. opensymphony. xwork2. DefaultActionInvocation @13d4497xwork. MethodAccessor. denyMethodExecution?笔者很懒,没有找Booleanfalsereport. conversion. errors?笔者很懒,没有找Booleanfalsesession?无org. apache. struts2. dispatcher. SessionMap{}com. opensymphony. xwork2. util. ValueStack. ValueStack?ValueStack.VALUE_STACKcom. opensymphony. xwork2. ognl. OgnlValueStackcom. opensymphony. xwork2. ognl. OgnlValueStack @16237fdrequest?无org. apache. struts2. dispatcher. RequestMap略action?笔者很懒,没有找com. example. MyAction略struts. actionMapping?笔者很懒,没有找org. apache. struts2. dispatcher. mapper. ActionMappingorg. apache. struts2. dispatcher. mapper. ActionMapping @892cc5parameters?无java. util. HashMap{}com. opensymphony. xwork2. ActionContext. parameters?ActionContext.PARAMETERSjava. 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类型的,并且会直接进行类型转换。于是,如果直接使用<s:property value="request"/>的话其实会让页面抛出异常:Request类型不能转换成String类型。所以,只能用如果不带#的话只能成功读取ActionContext中String类型的值。这种机制使得某些时候栈顶的属性可以覆盖ActionContext中的key,或许你正需要它。然而,鉴于这种机制的不确定性,笔者建议访问ActionContext中的数据一定要带上“#”,可以免去一些麻烦。
? ? 关于这种转型异常,笔者认为是Struts2的bug,源代码如下,当“value = getValue(expr, asType);”时,是考虑了asType的,但从context中读取时“value = findInContext(expr);”,就没有考虑asType,并且没有在其他地方看到类型检查操作:
?
?
Java代码?
?
- //?本代码截取Struts2.3.1.2版本com.opensymphony.xwork2.ognl.OgnlValueStack类的第340行-352行??
- ????private?Object?tryFindValue(String?expr,?Class?asType)?throws?OgnlException?{ ??
- ????????Object?value?=?null; ??
- ????????try?{ ??
- ????????????expr?=?lookupForOverrides(expr); ??
- ????????????value?=?getValue(expr,?asType); ??
- ????????????if?(value?==?null)?{ ??
- ????????????????value?=?findInContext(expr); ??
- ????????????} ??
- ????????}?finally?{ ??
- ????????????context.remove(THROW_EXCEPTION_ON_FAILURE); ??
- ????????} ??
- ????????return?value; ??
- ????}??
[java]?view plaincopy?
- //?本代码截取Struts2.3.1.2版本com.opensymphony.xwork2.ognl.OgnlValueStack类的第340行-352行??
- ????private?Object?tryFindValue(String?expr,?Class?asType)?throws?OgnlException?{??
- ????????Object?value?=?null;??
- ????????try?{??
- ????????????expr?=?lookupForOverrides(expr);??
- ????????????value?=?getValue(expr,?asType);??
- ????????????if?(value?==?null)?{??
- ????????????????value?=?findInContext(expr);??
- ????????????}??
- ????????}?finally?{??
- ????????????context.remove(THROW_EXCEPTION_ON_FAILURE);??
- ????????}??
- ????????return?value;??
- ????}??
?