前面已经了解到ActionContext是Action执行时的上下文,里面存放着Action在执行时须要用到的对象,咱们也称之为广义值栈。java
Struts2在每次执行Action以前都会建立新的ActionContext,在同一个线程里ActionContext里面的属性是惟一的,这样Action就能够在多线程中使用。安全
1:ActionContext的线程安全性session
那么Struts2是如何保证ActionContext的线程安全性呢?多线程
看看ActionContext对象的代码,示例以下:框架
1
2
3
4
|
public
class
ActionContext
implements
Serializable {
static
ThreadLocal actionContext =
new
ThreadLocal();
……
}
|
ThreadLocal又称为“线程局部变量”,它为每个使用该变量的线程都提供一个变量值的副本,使每个线程均可以独立地改变本身的副本,而不会和其它线程的副本冲突。 函数
存放在ActionContext里的数据都存放在这个ThreadLocal的属性中,而这个属性只会在对应的当前请求线程中可见,从而保证数据是线程安全的。 学习
2:访问的是Map 测试
回顾前面在使用ActionContext来访问Session中数据的程序,你会发现,其实在程序里面访问的是一个Map,而非HttpSession对象,这是为何呢? this
原来,Struts2框架将与Web相关的不少对象从新进行了包装,好比将HttpSession对象从新包装成了一个Map对象,里面存放着 Session中的数据,提供这个Map给Action使用,而不用Action直接和底层的HttpSession打交道。也正是由于框架的包装,让 Action能够彻底的和Web层解耦。 spa
可是要注意一点,ActionContext不能在普通的Java应用程序中使用。
在之前的学习中,介绍了Action和Servlet API是解耦的,所以能够在Java应用程序中调用Action的execute方法来进行测试。可是若是使用了ActionContext来获取 session数据,那么就不能这样运行了。由于ActionContext包装的都是Web的数据,在Java应用程序中运行的时候,没有Web的环境和响应的数据,于是会抛出空指针的异常。
访问其它的Web对象的值也是与此相似的,你经过ActionContext去访问的都是包装后的Map。
3:使用SessionAware接口
Struts2还提供另一种简单的方式,使用SessionAware接口来访问存储于ActionContext中的数据,该接口经过使用IoC/DI来为Action注入Session Map,就能够在程序里面直接使用这个Map来操做数据了。
(1)在Action中再也不须要访问ActionContext了,取而代之,Action实现SessionAware接口,该接口告知 Struts2在Action执行以前要设置Session Map,是经过servletConfig 拦截器来实现的,这个拦截器在defaultStack里面就有。示例代码以下:
1
2
3
4
5
6
7
8
9
10
11
12
|
public
class
OgnlAction
extends
ActionSupport
implements
SessionAware{
private
Map<String, Object> session;
@Resource
public
void
setSession(Map<String, Object> session) {
this
.session = session;
}
public
String execute(){
session.put(
"sessionTestKey"
,
"测试SessionAware"
);
return
this
.SUCCESS;
}
}
|
在上面的代码中:
为了可以在普通的Java应用中运行并测试Action,推荐你们使用SessionAware的方式来访问HttpSession。由于这样一来,在通 过main方法运行或测试的时候,能够直接调用setSession方法,传入模拟的会话数据,就不会出现execute方法中抛出空指针的异常了。
所以,推荐你们使用SessionAware的方式来访问HttpSession。
4:使用其它包装接口
跟SessionAware相似,你可使用RequestAware来获取包装请求对象的attribute中的值的Map; 使用ApplicationAware来获取包装ServletContext对象的attribute中的值的Map;使用 ParameterAware来获取包装请求对象的参数中的值的Map,等等,这里只罗列这几个常见和经常使用的,还有更多的请参见Struts2的API文 档。
在实际应用开发中,光是获取数据就够了吗?答案显然是否认的,有些时候,根据功能须要,在Action中必需要能获取到Servlet相关的API,好比要操做Cookie。这个时候,就须要用ServletActionContext了。
1:ServletActionContext概述
这个类直接继承了ActionContext,固然也继承了它父类的不少功能,好比:对OgnlValueStack、Action名字等的访问。更重要的是,它还提供了直接访问Servlet的相关对象的功能,它能够取得的对象有:
2:基本使用
直接使用ServletActionContext的静态方法就能够获取到相应的对象。示例以下:
1
2
3
4
5
|
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
ServletContext servletContext = ServletActionContext.getServletContext();
PageContext pageContext = ServletActionContext.getPageContext();
HttpSession session = ServletActionContext.getRequest().getSession();
|
这里要注意的是HttpSession对象的获取,是在取得HttpRequest对象事后,经过HttpRequest对象来获取会话对象。固然,取得相应的对象后,就直接使用这些对象的方法来进行开发,这里就不去赘述了。
3:经过IoC/DI的方式来获取相应的Servlet对象
还能够经过IoC/DI的方式来获取相应的Servlet对象,对应关系是:
用ServletRequestAware来示例一下。
(1)修改Action,让其实现ServletRequestAware接口,示例代码以下:
java代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
class
OgnlAction
extends
ActionSupport
implements
ServletRequestAware{
private
HttpServletRequest request =
null
;
@Resource
public
void
setServletRequest(HttpServletRequest request) {
this
.request = request;
}
public
String execute(){
request.setAttribute(
"request"
,
"Request的属性值"
);
request.getSession().setAttribute(
"sessionTestKey"
,
"测试SessionAware"
);
return
this
.SUCCESS;
}
}
|
根据前面的讲述,你会发现,ActionContext和ServletActionContext有着一些重复的功能,都可以获取到Web对象的数据,可是又有些不一样。
一般状况下,能够这么认为:ActionContext主要负责值的操做;ServletActionContext主要负责获取Servlet对象。
那么在Action中,该如何去抉择呢?建议的原则是:
总之,要尽可能让Action与Web无关,这对于Action的测试和复用都是极其有好处的。
另外还有一点须要注意:在使用ActionContext时,不要在Action的构造函数里使用ActionContext.getContext(),由于这个时候ActionContext里的一些值也许尚未设置,这时经过ActionContext取得的值也许是null。