struts2和spring的Action区别

       首先我们必须要先了解servlet的生命周期:

 

服务器只创建每个servlet的单一实例,首次创建servlet时,它的init方法会被调用,因此,init是放置一次性设置代码的地方,之后针对每个用户的请求都会创建一个线程,该线程调用前面创建的实例方法。多个并发请求一般会导致多个线程同时调用service(线程安全),service 方法会依据接受到HTTP请求的类型,调用doXXX方法。最后如果服务器卸载某个servlet就会调用servlet的destroy方法。

 

       servlet是否线程安全?

通常情况下,系统只生成servlet的单一实例之后,为每个用户请求建立新的线程。如果很多请求同时到来,那么多个线程可能会并发的访问同一个 servlet对象。因此我们必须小心地同步对字段以及全局变量和其它共享数据的访问,因为多个线程可能同时对同一数据进行访问。(多个线程并不共享局部 变量)。

       假设我们现在servlet定义了一个成员变量,在servlet初始化的时候,系统为该成员变量在堆中分配了内存空间,以后没每次访问服务器将不会再在堆里开辟内存空间,多人同时访问这个这个成员变量的时候,都会从同一块内存中获取。

 

       下面我们来了解下struts1,2及spring的线程安全问题。

        Struts1和Spring 都是单实例多线程,Struts2是多实例多线程的。Struts 2 Action对象为每一个请求产生一个实例,因此没有线程安全问题 。

      《strut2权威指南》中有这么一段: 
线程模式方面的对比:Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能做的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的;Struts 2 Action对象为每一个请求产生一个实例,因此没有线程安全问题 。

通过这文章知道,为什么struts1中并没有考虑到线程问题,因为所有的代码都是写在execute的方法中,所有变量都是定义在里面,所以没有线程安全问题。
而现在的struts2就不一样了。struts2的action中就像一个POJO一样,定义了很多的类变量。这就有线程安全问题了。。此时,就使用 scope=prototype来指定是个原型模式,而不是单例,这样就解决了线程安全问题。每个线程都是一个新的实例。。  所以相对来说,struts1 要多使用局部变量,而struts2使用 实例变量则不会产生不安全的结果。

 

spring的controller是单例的。

先看看spring的bean作用域有几种,分别有啥不同。

 

spring bean作用域有以下5个:

 

singleton:单例模式,当spring创建applicationContext容器的时候,spring会欲初始化所有的该作用域实例,加上lazy-init就可以避免预处理;

 

prototype:原型模式,每次通过getBean获取该bean就会新产生一个实例,创建后spring将不再对其管理;

 

====下面是在web项目下才用到的===

 

request:搞web的大家都应该明白request的域了吧,就是每次请求都新产生一个实例,和prototype不同就是创建后,接下来的管理,spring依然在监听

 

session:每次会话,同上

 

global session:全局的web域,类似于servlet中的application

 

好了,上面都说了spring的controller默认是单例,那很自然就是singleton了。

 

       如果现在我们拿spring来管理struts2的话,那么默认的类就是单例的,如:

 

默认是单例,比如这样
<bean id="foo1" class="demo.scope.Foo" />
不指明scope就是单例的
如下这样配置的就是原型,每次生成bean的时候都新建一个实例
<bean id="foo2" class="demo.scope.Foo" scope="prototype"/>

 

      Spring是通过ThreadLocal来实现线程安全的。如果一个对象要被多个线程访问。而该对象存在类变量被不同类方法读写,为获得线程安全,可以使用ThreadLocal来代替类变量。

我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都 可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、 TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal 进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。  一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。 在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,如图9‑2所示:   这样你就可以根据需要,将一些非线程安全的变量以ThreadLocal存放,在同一次请求响应的调用线程中,所有关联的对象引用到的都是同一个变量。   由于①处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:   

代码清单4 TopicDao:线程安全

 import java.sql.Connection; 

  import java.sql.Statement; 

  public class TopicDao { 

  ①使用ThreadLocal保存Connection变量 

  private static ThreadLocal connThreadLocal = new ThreadLocal(); 

  public static Connection getConnection(){ 

  ②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection, 

  并将其保存到线程本地变量中。 

  if (connThreadLocal. get() == null) { 

  Connection conn = ConnectionManager.getConnection(); 

  connThreadLocal.set(conn); 

  return conn; 

  }else{ 

  return connThreadLocal. get();③直接返回线程本地变量 

  } 

  } 

  public void addTopic() { 

  ④从ThreadLocal中获取线程对应的Connection 

  Statement stat = getConnection().createStatement(); 

  } 

  } 
 

 不同的线程在使用TopicDao时,先判断connThreadLocal.是否是null,如果是null,则说明当前线程还没有对应的Connection对象,这时创建一个Connection对象并添加到本地线程变量中;如果不为null,则说明当前的线程已经拥有了Connection对象,直接使用就可以了。这样,就保证了不同的线程使用线程相关的Connection,而不会使用其它线程的Connection。因此,这个TopicDao就可以做到singleton共享了。

猜你喜欢

转载自zy116494718.iteye.com/blog/2333759