论坛首页 入门技术论坛

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

浏览 7768 次
该帖已经被评为新手帖
作者 正文
   发表时间:2010-07-06   最后修改:2010-07-10
大家都知道,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实例。

   发表时间:2010-07-06  
引用
大家都知道,struts1.2由于是线程安全的,每一个请求都去实例化一个action,造成大量并发时的资源浪费。
  struts2在这一点上做了改进,每个action都是一个singleton,所有的请求都是请求同一个action实例。


struts1.2不了解
struts2怎么会是所有的请求都是请求同一个action实例
0 请登录后投票
   发表时间:2010-07-06  
struts1 貌似是单实例的 所以会有线程安全问题
struts2 貌似是每个请求都有一个实例 所以不会有线程安全的问题
0 请登录后投票
   发表时间:2010-07-06   最后修改:2010-07-06
LZ搞反了吧

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

struts2是每次创建新的实例,其请求变量是绑定在action的实例变量里面,用spring管理S2的action时,因为spring的bean默认是只创建一个实例,所以必须设定作用域为prototype才行
0 请登录后投票
   发表时间:2010-07-06  
楼主反了额
0 请登录后投票
   发表时间:2010-07-07  
果然是反了
0 请登录后投票
   发表时间:2010-07-07  
aws 写道
LZ搞反了吧

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

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



我说也是LZ搞反了
0 请登录后投票
   发表时间:2010-07-07  
我就说怎么一看感觉变扭。。
0 请登录后投票
   发表时间:2010-07-07  
struts1来一个请求,通过key(action)到actionMap里找,找到null就添加一个创建,否则直接使用。
0 请登录后投票
   发表时间:2010-07-07  
lz误人子弟啊,我这种菜鸟就是被这么误掉的,哈哈
0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics