如果在action 中直接定义实例变量,问题就很大了。原因其实也很简单:
引用
为了确保线程安全(thread-safe),在一个应用的生命周期中,Struts框架只会为每个Action类创建一个Action实例。所有的客户请求共享一个Action实例,并且所有请求线程可以同时执行它的execute()方法。
所以,每个action只有一个实例, 在action打印this也可以看到, 确实是一样的, 所以, 不要在Action里面生命全局的变量记忆数据, 没有意义而且不安全。
使用actionForm则不会有问题,原因也很简单:
actionForm 是通过参数形式传入action的,不存在共享变量的问题,其实每个request产生的actionForm实例也是不同的。
尽管这年代已经使用 spring +tapestry或 jsf了,java也有线程同步机制,不过线程安全性还是需要考虑的。
因为Action实例应该是线程安全的,所以对应用程序的每个Action类只能创建一个单独的实例。所有的客户请求共享一个实例,并且可以同时调用exectue()方法。
RequestProcessor包含一个Hashmap,它的键是在配置文件中指定的所有Action类的名字;每个键的值都是这个Action的单个实例。RequestProcessor类的RequestProcessor方法中,框架包检测HashMap来看是否已经创建了实例。如果是,实例将被返回。否则,会创建Action类的一个新实例,并存储到HashMap中。然后返回。创建新Action实例的代码部分被同步化以便确保只有一个线程能够创建实例。一旦线程创建了实例,并把它插入到HashMap中,所有将来的线程将从高速缓存中使用这个实例。
在框架包中,为每个Action类创建一个单独的Action实例。每个客户请求将共享同一实例,与每个客户请求共享相同的ActionSevlet实例一样。因此,与servlet一样,必须确保你的Action类在多线程环境中正确运行。
为了达到线程安全状态,确保你的Action类不使用实例变量了存储客户特有的状态是非常重要的。
你可以使用实例变量存储状态信息,但它不应该是客户或请求存储状态。例如,你可以创建一个org.apache.commons.logging.Log类型的实例变量在logger中存储信息。logger是线程安全的,它不为特有的客户或请求存储状态。
客户特有的状态,应该在execute()方法内声明这些变量。这些局部变量分配到与实例变量不同的内存空间中。进入exectue()中的每个线程都有自己的局部变量堆栈,因此,没有机会重载其他线程的状态。