`
jakoes
  • 浏览: 68048 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

spring源码分析-controller的线程安全

阅读更多
大家都知道,struts1.2由于是线程安全的,每一个请求都去实例化一个action,造成大量并发时的资源浪费。
  struts2在这一点上做了改进,每个action都是一个singleton,所有的请求都是请求同一个action实例。这样在一定程度上能节约资源,但又有安全问题。最常见的就是在action中声明有块状的实例变量,因为这一点是不被提倡的。如果一定要声明,那一定要加上同步块。
  那么在spring mvc中的controller是不是线程安全的呢?答案是否定的。controller在默认情况下也是非线程安全的,我们来看看源码:

 * @author John A. Lewis
 * @author Juergen Hoeller
 * @since 2.0
 * @see ResourceAwareController
 * @see EventAwareController
 */
public abstract class AbstractController extends PortletContentGenerator implements Controller {

	[color=red]private boolean synchronizeOnSession = false;[/color]



由上面源码可知,controller默认是非安全的。


	
public void handleActionRequest(ActionRequest request, ActionResponse response) throws Exception {
		// Delegate to PortletContentGenerator for checking and preparing.
		check(request, response);

		// Execute in synchronized block if required.
                //只有synchronizeOnSession设置为true,才会同步处理请求
		if (this.synchronizeOnSession) {
			PortletSession session = request.getPortletSession(false);
			if (session != null) {
				synchronized (session) {
					handleActionRequestInternal(request, response);
					return;
				}
			}
		}

		handleActionRequestInternal(request, response);
	}



只有手工设置controller的synchronizeOnSession值为true,才会被同步处理。

因此,我们在使用spring mvc 的contrller时,应避免在controller中定义实例变量。

##### 更正 ####################################
由于在下对struts1.x的理解也来自网络,给大家带来不便,还请见谅。
经过对struts1.x源码的研读发现:
struts1.2获取action的方式是单例的,所有的action都被维护在一个hashMap里,当有请求到达时,先根据action的名称去hashMap里查找要请求的Action是否已经存在,如果存在,则直接返回hashMap里的action。如果不存在,则创建一个新的Action实例。


下面我们来分析一下源码:

请求到达ActionServlet时,首先会到达doGet()或doPost()方法,而ActionServlet转给了process()方法进行统一处理


    public void doPost(HttpServletRequest request,
               HttpServletResponse response)
        throws IOException, ServletException {

        process(request, response);

    }





而process()方法又会转给processor的process()方法进行处理

    protected void process(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {

        ...
        processor.process(request, response);

    }




processor的process()方法里经过一系列处理后,最后通过processActionCreate方法来返回一个具体的action实例


public void process(HttpServletRequest request,
                        HttpServletResponse response)
        throws IOException, ServletException {

        ...

        // Create or acquire the Action instance to process this request
        Action action = processActionCreate(request, response, mapping);
        if (action == null) {
            return;
        }

     ...

    }




那我们就到processActionCreate这个方法里去一窥究竟吧:
1、先获取类名
2、根据类名去map里查寻实例是否已经存在
3、如果存在,则直接返回
4、如果不存在,则创建一个新实例
5、把创建好的action放到map里备用

  
protected Action processActionCreate(HttpServletRequest request,
                                         HttpServletResponse response,
                                         ActionMapping mapping)
        throws IOException {

        // Acquire the Action instance we will be using (if there is one)
        String className = mapping.getType();//1、先获取类名 
        ...
        Action instance = null;
        synchronized (actions) {

            // Return any existing Action instance of this class
            instance = (Action) actions.get(className);//2、根据类名去map里查寻实例是否已经存在
            if (instance != null) {
                return (instance); //3、如果存在,则直接返回
            }

            // Create and return a new Action instance
            //4、如果不存在,则创建一个新实例
            instance = (Action) RequestUtils.applicationInstance(className)

            instance.setServlet(this.servlet);
            actions.put(className, instance);//5、把创建好的action放到map里
        }
        ...
        return (instance);

    }



我们再来看看actions的定义:

    /**
     * The set of Action instances that have been created and
     * initialized, keyed by the fully qualified Java class name of the
     * Action class.
     */
    protected HashMap actions = new HashMap();


结论:
struts1.2获取action的方式是单例的,所有的action都被维护在一个hashMap里,当有请求到达时,先根据action的名称去hashMap里查找要请求的Action是否已经存在,如果存在,则直接返回hashMap里的action。如果不存在,则创建一个新的Action实例。

分享到:
评论
17 楼 颓废主义 2013-11-14  
楼主敢不敢把你的原文删除了,或者把更正的内容放后边,然后附上原文,你这样误人子弟都一年了。
16 楼 fddjxllren 2012-03-23  
struts2是单例的可以改变设定作用域为scope="prototype"改为原型模式就可以避免安全问题
15 楼 fddjxllren 2012-03-23  
楼主误人子弟呀,struts1就不存在线程安全问题他所有的方法都是定义在execute里面,struts2是多线程请求的单例,所以有线程安全问题.只要不定义实例变量,静态变量,必须设定作用域为prototype,或扩长requestprocess
14 楼 citymoon2000 2011-10-16  
说反了,差点误导我了
13 楼 accpchf 2011-06-16  
现在的web mvc的请求控制器(action或controller)的实例化有这么三种,一种是作为全局对象的单例方式,第二是全局对象的prototype方式,第三种是局部对象的方式。
在用第一种方式的时候,如果把请求控制器直接暴露给开发者,那么肯定是线程不安全的,也就是说,单例的实例变量,会被多线程共享,造成不安全。所以当把请求控制器直接暴露给开发者时候,避免线程安全的解决方式之一,就是把这个全局的请求控制器设置为prototype,可是会造成额外的开销(要精打细算吗)。
第三种方式把请求的逻辑对象,“插入”到底层的请求控制器里,那个底层的请求控制器不用开发者去关注,类似webWork,因为“插入”的逻辑对象是"局部变量",所以线程安全,不过也会有开销的问题(不用那么精打细算吧)。
具体有下面几种情况:
1.servlet 的实例化是在容器里,是单例,第一种设计方式。
1.struts1的action,都会放在一个map里面管理,单例,第一种设计方式。
2.spring mvc的controller,注入到spring 的bean容器里。是单例,第一种设计方式。
3.webWork早期的版本,是基于ServletDispatcher,是继承HttpServlet,即对servlet的封装。
servlet虽然是单例,ServletDispatcher 本身不会像struts1,spring mvc那样,直接把servlet的封装暴露给开发者,而是让开发者去写好acton,ServletDispatcher 处理services方法时,调用那些acton就行了。这里就不存在servlet的线程安全问题。第三种方式。
3.webWork现在的版本(struts2)。抛弃了ServletDispatcher,而用filterDispatcher,这个filter的封装。而基于filter的封装,因为可以把请求控制器直接当成一个普通的pojo,由filter调用请求控制器(action)进来处理。所以每次处理请求,new 一次 请求控制器,就不会有线程安全的问题。第三种方式。
4.当struts2和spring整合的时候,需要注入action到spring中去,spring默认的实例action的方式是单例,所以这样会有线程安全的问题,因为,struts2的acton里的实例变量,经常是用来接收http请求参数的,所以配置的时候,一定要设置成prototype。第二种设计方式。
5.不过,如果用了struts2-spring 插件的时候,这个插件对action的实例,默认是prototype,不需要设置。第一种设计方式。但是可以设置为prototype。第二种设计方式。

12 楼 accpchf 2011-06-16  
楼主,误人子弟
11 楼 cppmayi 2010-07-07  
别误人子弟,赶紧修改。struts2是多例
10 楼 wnick 2010-07-07  
jakoes 写道
  大家都知道,struts1.2由于是线程安全的,每一个请求都去实例化一个action,造成大量并发时的资源浪费。
  struts2在这一点上做了改进,每个action都是一个singleton,所有的请求都是请求同一个action实例。这样在一定程度上能节约资源,但又有安全问题。最常见的就是在action中声明有块状的实例变量,因为这一点是不被提倡的。如果一定要声明,那一定要加上同步块。
 


反了哦
struts2 大部分是每次实例化
struts1 是单粒的
9 楼 yunzhu 2010-07-07  
lz误人子弟啊,我这种菜鸟就是被这么误掉的,哈哈
8 楼 lydawen 2010-07-07  
struts1来一个请求,通过key(action)到actionMap里找,找到null就添加一个创建,否则直接使用。
7 楼 zy2419 2010-07-07  
我就说怎么一看感觉变扭。。
6 楼 rentianchou 2010-07-07  
aws 写道
LZ搞反了吧

struts1是单例提供服务,请求数据绑定在actionform里,form当做参数传入调用方法,不用实例变量就不会出现并发问题---spring mvc的contrller也是这样的模式

struts2是每次创建新的实例,其请求变量是绑定在action的实例变量里面,用spring管理S2的action时,因为spring的bean默认是只创建一个实例,所以必须设定作用域为prototype才行



我说也是LZ搞反了
5 楼 love_ai87 2010-07-07  
果然是反了
4 楼 wangr1984 2010-07-06  
楼主反了额
3 楼 aws 2010-07-06  
LZ搞反了吧

struts1是单例提供服务,请求数据绑定在actionform里,form当做参数传入调用方法,不用实例变量就不会出现并发问题---spring mvc的contrller也是这样的模式

struts2是每次创建新的实例,其请求变量是绑定在action的实例变量里面,用spring管理S2的action时,因为spring的bean默认是只创建一个实例,所以必须设定作用域为prototype才行
2 楼 hepeng555 2010-07-06  
struts1 貌似是单实例的 所以会有线程安全问题
struts2 貌似是每个请求都有一个实例 所以不会有线程安全的问题
1 楼 waitingmyself 2010-07-06  
引用
大家都知道,struts1.2由于是线程安全的,每一个请求都去实例化一个action,造成大量并发时的资源浪费。
  struts2在这一点上做了改进,每个action都是一个singleton,所有的请求都是请求同一个action实例。


struts1.2不了解
struts2怎么会是所有的请求都是请求同一个action实例

相关推荐

Global site tag (gtag.js) - Google Analytics