1. 程式人生 > >spring原始碼分析-controller的執行緒安全

spring原始碼分析-controller的執行緒安全

大家都知道,struts1.2由於是執行緒安全的,每一個請求都去例項化一個action,造成大量併發時的資源浪費。 
  struts2在這一點上做了改進,每個action都是一個singleton,所有的請求都是請求同一個action例項。這樣在一定程度上能節約資源,但又有安全問題。最常見的就是在action中宣告有塊狀的例項變數,因為這一點是不被提倡的。如果一定要宣告,那一定要加上同步塊。 
  那麼在spring mvc中的controller是不是執行緒安全的呢?答案是否定的。controller在預設情況下也是非執行緒安全的,我們來看看原始碼: 

Java程式碼  收藏程式碼
  1.  * @author
     John A. Lewis  
  2.  * @author Juergen Hoeller  
  3.  * @since 2.0  
  4.  * @see ResourceAwareController  
  5.  * @see EventAwareController  
  6.  */  
  7. public abstract class AbstractController extends PortletContentGenerator implements Controller {  
  8.     [color=red]private boolean synchronizeOnSession = false;[/color]  



由上面原始碼可知,controller預設是非安全的。 



Java程式碼  收藏程式碼
  1. public void handleActionRequest(ActionRequest request, ActionResponse response) throws Exception {  
  2.         // Delegate to PortletContentGenerator for checking and preparing.  
  3.         check(request, response);  
  4.         // Execute in synchronized block if required.  
  5.                 //只有synchronizeOnSession設定為true,才會同步處理請求
      
  6.         if (this.synchronizeOnSession) {  
  7.             PortletSession session = request.getPortletSession(false);  
  8.             if (session != null) {  
  9.                 synchronized (session) {  
  10.                     handleActionRequestInternal(request, response);  
  11.                     return;  
  12.                 }  
  13.             }  
  14.         }  
  15.         handleActionRequestInternal(request, response);  
  16.     }  



只有手工設定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()方法進行統一處理 


Java程式碼  收藏程式碼
  1. public void doPost(HttpServletRequest request,  
  2.            HttpServletResponse response)  
  3.     throws IOException, ServletException {  
  4.     process(request, response);  
  5. }  





而process()方法又會轉給processor的process()方法進行處理 

Java程式碼  收藏程式碼
  1. protected void process(HttpServletRequest request, HttpServletResponse response)  
  2.     throws IOException, ServletException {  
  3.     ...  
  4.     processor.process(request, response);  
  5. }  




processor的process()方法裡經過一系列處理後,最後通過processActionCreate方法來返回一個具體的action例項


Java程式碼  收藏程式碼
  1. public void process(HttpServletRequest request,  
  2.                         HttpServletResponse response)  
  3.         throws IOException, ServletException {  
  4.         ...  
  5.         // Create or acquire the Action instance to process this request  
  6.         Action action = processActionCreate(request, response, mapping);  
  7.         if (action == null) {  
  8.             return;  
  9.         }  
  10.      ...  
  11.     }  




那我們就到processActionCreate這個方法裡去一窺究竟吧: 
1、先獲取類名 
2、根據類名去map裡查尋例項是否已經存在 
3、如果存在,則直接返回 
4、如果不存在,則建立一個新例項 
5、把建立好的action放到map裡備用 

Java程式碼  收藏程式碼
  1. protected Action processActionCreate(HttpServletRequest request,  
  2.                                          HttpServletResponse response,  
  3.                                          ActionMapping mapping)  
  4.         throws IOException {  
  5.         // Acquire the Action instance we will be using (if there is one)  
  6.         String className = mapping.getType();//1、先獲取類名   
  7.         ...  
  8.         Action instance = null;  
  9.         synchronized (actions) {  
  10.             // Return any existing Action instance of this class  
  11.             instance = (Action) actions.get(className);//2、根據類名去map裡查尋例項是否已經存在  
  12.             if (instance != null) {  
  13.                 return (instance); //3、如果存在,則直接返回  
  14.             }  
  15.             // Create and return a new Action instance  
  16.             //4、如果不存在,則建立一個新例項  
  17.             instance = (Action) RequestUtils.applicationInstance(className)  
  18.             instance.setServlet(this.servlet);  
  19.             actions.put(className, instance);//5、把建立好的action放到map裡  
  20.         }  
  21.         ...  
  22.         return (instance);  
  23.     }  



我們再來看看actions的定義: 

Java程式碼  收藏程式碼
  1. /** 
  2.  * The set of Action instances that have been created and 
  3.  * initialized, keyed by the fully qualified Java class name of the 
  4.  * Action class. 
  5.  */  
  6. protected HashMap actions = new HashMap();  


結論: 
struts1.2獲取action的方式是單例的,所有的action都被維護在一個hashMap裡,當有請求到達時,先根據action的名稱去hashMap裡查詢要請求的Action是否已經存在,如果存在,則直接返回hashMap裡的action。如果不存在,則建立一個新的Action例項。