欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Struts2 ActionContext 中的数据详解

程序员文章站 2024-03-13 14:37:57
actioncontext     actioncontext是action的上下文,struts2自动在其中保存了一些在action执行过...

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的操作  
publicclass 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进行操作  
publicclass 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> 
    <metahttp-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:propertyvalue="#person"/> 
    <!-- 获得person的name属性,如下 --> 
    <s:propertyvalue="#person.name"/> 
    <!-- 获得struts2在actioncontext中存入的值,比如request的map,如下 --> 
    <s:propertyvalue="#request"/> 
    <!-- 获得struts2在actioncontext中存入的值,比如session的map,如下 --> 
    <s:propertyvalue="#session"/> 
    <!-- 获得struts2在actioncontext中存入的值,request请求传递的get参数或post参数的map,如下 --> 
    <s:propertyvalue="#parameters"/> 
      
    <!-- 以下演示在jsp中把值存入actioncontext中 --> 
    <!-- 存入一个字符串"myname",key为"mykey",如下 --> 
    <s:setvalue="%{'myname'}"var="mykey"/> 
    <!-- 使用s:bean标签来创建一个对象,并把它存入actioncontext中,key为myobject,如下 --> 
    <s:beanname="com.example.person"var="myobject"/> 
    <!-- 之后就可以用“#”来读取它们,如下 --> 
    <s:propertyvalue="#mykey"/> 
    <s:propertyvalue="#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传值。

一些例子:

// 本类将演示拦截器中对httpservletrequest和request的map的操作  
publicclass 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();  
  }  
} 
// 本类将演示在action中对httpservletrequest和request的map进行操作(静态方法获得actioncontext) 
publicclass 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;  
  }  
} 
// 本类将演示在action中使用servletrequestaware获得httpservletrequest(注意:要使用servletconfiginterceptor拦截器) 
publicclass myaction extends actionsupport implements servletrequestaware {  
 
  private httpservletrequest request;  
    
  //此方法是接口servletrequestaware的方法  
  publicvoid setservletrequest(httpservletrequest request) {  
    this.request = request;  
  }  
 
  @override 
  public string execute() throws exception {  
    // httpservletrequest已在该类的字段中准备好,可直接使用 
    // ......  
    return success;  
  }  
} 
// 本类将演示在action中使用servletrequestaware获得request的map(注意:要使用servletconfiginterceptor拦截器) 
publicclass myaction extends actionsupport implements requestaware {  
 
  map<string, object> request;  
 
  // 该方法是接口requestaware的方法  
  publicvoid setrequest(map<string, object> request) {  
    this.request = request;  
  }  
 
  @override 
  public string execute() throws exception {  
    // request的map已在该类的字段中准备好,可直接使用  
    // ......  
    return success;  
  }  
} 
<!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。