当前位置: 移动技术网 > IT编程>开发语言>Java > Struts2 ActionContext 中的数据详解

Struts2 ActionContext 中的数据详解

2019年07月22日  | 移动技术网IT编程  | 我要评论

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。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网