Servlet的生命周期是“初始化->init->service->destroy->卸载”。

这里大家都知道,我们在web.xml里面定义一个servlet的时候,我们能够给他们设置一个“load-on-startup” 的值。假设 Servlet 的 load-on-startup 配置项大于 0,那么在 Context 容器启动的时候就会被实例化。而且tomcat给每个servlet载入而且实例化一个对象(注解:也就是说。我们用户在web.xml里面配置的每个servlet都会被实例成一个servlet对象)

a, 以下的配置表示会有两个servlet对象被实例化。即使他们相应的是同一个servlet class

1 <?xml version="1.0" encoding="UTF-8"?>
2 <web-app version="2.5"
3 xmlns="http://java.sun.com/xml/ns/javaee"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
6 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
7 <servlet>
8 <servlet-name>ServletTest1</servlet-name>
9 <servlet-class>web.servlet.ServletTest1</servlet-class>
10 </servlet>
11
12 <servlet-mapping>
13 <servlet-name>ServletTest1</servlet-name>
14 <url-pattern>/servlet/ServletTest1</url-pattern>
15 </servlet-mapping>
16
17 <servlet>
18 <servlet-name>ServletTest2</servlet-name>
19 <servlet-class>web.servlet.ServletTest1</servlet-class>
20 </servlet>
21
22 <servlet-mapping>
23 <servlet-name>ServletTest2</servlet-name>
24 <url-pattern>/servlet/ServletTest1</url-pattern>
25 </servlet-mapping>
26 </web-app>


b, 以下的配置表示仅仅会有一个servlet被实例化

1 <?xml version="1.0" encoding="UTF-8"?>
2 <web-app version="2.5"
3 xmlns="http://java.sun.com/xml/ns/javaee"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
6 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
7 <servlet>
8 <servlet-name>ServletTest1</servlet-name>
9 <servlet-class>web.servlet.ServletTest1</servlet-class>
10 </servlet>
11
12 <servlet-mapping>
13 <servlet-name>ServletTest1</servlet-name>
14 <url-pattern>/servlet/ServletTest1</url-pattern>
15 </servlet-mapping>
16
17 <servlet-mapping>
18 <servlet-name>ServletTest1</servlet-name>
19 <url-pattern>/servlet/ServletTest1</url-pattern>
20 </servlet-mapping>
21 </web-app>


Tomcat容器对Servlet的实现採用的是单例模式,对于一个Servlet类,永远仅仅有一个servlet对象存在

1. Struts1

 Struts1是对Java web servlet接口的直接实现,即继承了tomcat对Servlet的实现。每个struts1里面的action都相应一个的是一个servlet class。所以这里的action在被Tomcat实例化之后也是单例的。这就是struts1产生多线程问题的解决办法

2. Struts2

上面我们了解了Struts1里面的多线程问题,那Struts2是怎么解决问题的呢?事实上道理很easy,原因就是Strtus2会获取到用户的http请求。然后负责给每一个请求实例化一个Action 对象,可是大家注意。这里的action对象和Struts1里面的action对象全然不是一个概念。struts1里面的action类就是一个servlet类。而这里的action类仅仅是一个普通的java class。这也就是为什么Struts1里面的action是线程不安全的,而struts2里面的action是线程安全的原因。

那我们在回头来看看struts2对servlet的处理和struts1有什么不同。看过前面分析的读者肯定知道,struts1的 action对servlet没有进行不论什么的包装,它是直接实现的Java WEB API 里面的servlet 接口。

所以才会有线程安全的问题,可是struts2底层帮我们封装了Servlet,使开发者不用直接接触Servlet。

详细做法是:Strtus2截获servlet请求,然后给每一个请求实例化一个Action对象,请求结束之后销毁Action对象。

------------------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------------------

在Struts1中的Action安全问题具体解释:

      假设你的代码所在的进程中有多个线程在同一时候执行,而这些线程可能会同一时候执行这段代码。假设每次执行结果和单线程执行的结果是一样的,并且其它的变量的值也和预期的是一样的,就是线程安全的。

  或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的运行结果存在二义性,也就是说我们不用考虑同步的问题。

  线程安全问题都是由全局变量及静态变量引起的。

若每一个线程中对全局变量、静态变量仅仅有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同一时候运行写操作,一般都须要考虑线程同步。否则就可能影响线程安全。

 Action类

      Action类真正实现应用程序的事务逻辑。它们负责处理请求。

在收到请求后。ActionServlet会:为这个请求选择适当的Action,假设须要,创建Action的一个实例,调用Action的perform()方法,假设ActionServlet不能找到有效的映射,它会调用默认的Action类(在配置文件里定义)。

假设找到了ActionServlet将适当的ActionMapping类转发给Action,这个Action使用ActionMapping找到本地转发。然后获得并设置ActionMapping属性。

依据servlet的环境和被覆盖的perform()方法的签名,ActionServlet也会传送ServletRequest对象或HttpServletRequest对象。

  全部Action类都扩展org.apache.struts.action.Action类。而且覆盖类中定义的某一个perform()方法。有两个perform()方法:

  处理非HTTP(一般的)请求:

<span style="font-family:Microsoft YaHei;font-size:14px;">public ActionForward perform(ActionMapping action,
AcionForm form,
ServletRequest request,
ServletResponse response)
throws IOException,ServletException
</span>


  处理HTTP请求:

<span style="font-family:Microsoft YaHei;font-size:14px;">public ActionForward perform(ActionMapping action,
AcionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException,ServletException </span>


Action类必须以“线程安全”的方式进行编程,由于控制器会令多个同一时候发生的请求共享同一个实例,对应的,在设计Action类时就须要注意下面几点:

 不能使用实例或静态变量存储特定请求的状态信息,它们会在同一个操作中共享跨越请求的全局资源,假设要訪问的资源(如JavaBean和会话变量)在并行訪问时须要进行保护,那么訪问就要进行同步,Action类的方法

  除了perform()方法外,还有下面方法:

  能够获得或设置与请求相关联的区域:

<span style="font-family:Microsoft YaHei;font-size:14px;">public Locale getLocale(HttpServletRequest request)

public void setLocale(HttpServletRequest request,Localelocale) </span>


  为应用程序获得消息资源:public MessageResources getResources()

  检查用户是否点击表单上的“取消”键,假设是,将返回true:public Boolean isCancelled(HttpServletRequest request)

  当应用程序错误发生时,Action类可以使用以下方法存储错误信息:

public void saveErrors(HttpServletRequestrequest,ActionErrors errors)

ActionError实例被用来存储错误信息。这种方法在错误keyword下的请求属性列表中存储ActionError对象。通过使用在struts标记库中定义的自己定义标记,JSP页可以显示这些错误信息,稍后我们就介绍。


 ActionForm类

  框架如果用户在应用程序中为每一个表单都创建了一个ActionForm bean。对于每一个在struts-config.xml文件里定义的bean。框架在调用Action类的perform()方法之前会进行下面操作:

       (1)在相关联的keyword下,它检查用于适当类的bean实例的用户会话,假设在会话中没有可用的bean,它就会自己主动创建一个新的bean并加入到用户的会话中。

       (2)对于请求中每一个与bean属性名称相应的參数,Action调用相应的设置方法。

        (3)当Action perform()被调用时,最新的ActionForml bean传送给它,參数值就能够马上使用了。

         (4)ActionForm类扩展org.apache.struts.action.ActionForm类。程序开发者创建的bean可以包括额外的属性。并且ActionServlet可能使用反射(同意从已载入的对象中回收信息)訪问它。

ActionForm类提供了还有一种处理错误的手段。提供两个方法:

<span style="font-family:Microsoft YaHei;font-size:14px;">Public ActionErrors validate(ActionMappin mapping,
ServletRequest request)
Public ActionErrors validate(ActionMappin mapping,
HttpServletRequest request) </span>


  你应该在自己的bean里覆盖validate()方法。并在配置文件中设置<action>元素的validate为true。在ActionServlet调用Action类前,它会调用validate(),假设返回的ActionErrors不是null,则ActinForm会依据错误keyword将ActionErrors存储在请求属性列表中。

  假设返回的不是null。并且长度大于0,则依据错误keyword将实例存储在请求的属性列表中,然后ActionServlet将响应转发到配置文件<action>元素的input属性所指向的目标。

  假设须要运行特定的数据有效性检查。最好在Action类中进行这个操作,而不是在ActionForm类中进行。

  方法reset()可将bean的属性恢复到默认值:

<span style="font-family:Microsoft YaHei;font-size:14px;">public void reset(ActionMapping mapping,HttpServletRequestrequest)

public void reset(ActionMapping mapping,ServletRequestrequest) </span>


  典型的ActionFrom bean仅仅有属性的设置与读取方法(getXXX),而没有实现事务逻辑的方法。

仅仅有简单的输入检查逻辑,使用的目的是为了存储用户在相关表单中输入的最新数据。以便能够将同一网页进行再生,同一时候提供一组错误信息,这样就能够让用户改动不对的输入数据。而真正对数据有效性进行检查的是Action类或适当的事务逻辑bean。

3.6 ActionForward类

  目的是控制器将Action类的处理结果转发至目的地。

Action类获得ActionForward实例的句柄,然后可用三种方法返回到ActionServlet,所以我们能够这样使用findForward():

 ActionServlet依据名称获取一个全局转发,ActionMappin实例被传送到perform()方法,并依据名称找到一个本地转发,还有一种是调用以下的一个构造器来创建它们自己的一个实例:

<span style="font-family:Microsoft YaHei;font-size:14px;">public ActionForward()

public ActionForward(String path)

public ActionForward(String path,Boolean redirect)</span>


----------------------------------------------------------------------------------------------------------------

在struts应用的生命周期中RequestProcessor仅仅能保证一个Action实例,全部的其它请求都共享这个实例,因此Aciton是多线程进行工作,可是有一个保证正常和安全工作的前提是,Action不能有这种实例变量。拥有状态的实例,尤其是拥有业务对象状态的实例. 假设要用到那些有状态的实例,唯一和最好的办法是在Action类中,仅在Action类的execute()方法中使用局部变量,对于每一个调用 execute()方法的线程,JVM会在每一个线程的堆栈中创建局部变量,因此每一个线程拥有独立的局部变量,不会被其它线程共享.当线程运行完 execute()方法后。它的局部变量就会被销毁.

如今项目中的做法:Action调用一个业务用例服务,这个服务本身没有变量,这个服务来保证实现一个模型层的业务逻辑.假设Action类的实例变量是必须设定的话,须要採用JAVA同步机制对訪问共享资源的代码块进行同步.

----------------------------------------------------------------------------------------------------------------------



struts的action是非线程安全的。不要在action中定义实例变量。

在spring的配置文件里,能够配置action为线程安全。即每次调用都生成一个新的实例,而不是仅仅用一个实例。

bean中设置singleton="true"


由于控制器会令多个同一时候发生的请求共享同一个实例,所以最好不要在Action中使用实例或静态变量存储特定请求的状态信息。假设要訪问相关的资源。最好进行同步操作。

 


版权声明:本文博客原创文章。博客,未经同意,不得转载。