1. 程式人生 > >Spring中的Controller是單例的(原因和問題)

Spring中的Controller是單例的(原因和問題)

在Spring3中,Controller預設是單例的。如果Controller中有個私有的變數a,那麼所有請求到Controller中,使用的a變數是共用的。即某個請求要是修改了這個變數a,那麼在別的請求中也是可以讀到這個修改的內容的。<p>若是在@controller之前增加@Scope("prototype"),就可以改變單例模式為多例模式。</p>
package com.xcesys.extras.webapp.controller.system;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "admin/system/example")
public class ExampleAction {
   //定義一個變數
	private int singletonInt = 1;
	
	@RequestMapping(value="/test")
	@ResponseBody
	public String singleton(HttpServletResponse response,HttpServletRequest request){
		String data = request.getParameter("data");
		if(data != null && data.length()>0){
			int paramInt = Integer.parseInt(data);
			singletonInt = singletonInt + paramInt;
		}else{
			singletonInt+=1000;
		}
		return String.valueOf(singletonInt);		
	}
}

輸入:http://localhost:8080/a/admin/system/example/test?data=15
輸出的結果是: <p>第一次: singletonInt=15</p><p>第二次: singletonInt=30</p><p>第三次: singletonInt=45</p>從以上結果可以得知,singletonInt的狀態是共享的,因此Controller是單例的。
問題:如果Controller類是單例,那麼多個執行緒請求同一個Controller類中的同一個方法,執行緒是否會堵塞
@RequestMapping(value="/sleep")
	@ResponseBody
	public String switcher(HttpServletRequest request,HttpServletResponse response) throws InterruptedException{
		String sleep = request.getParameter("sleep");
		if(sleep.equals("on")){
		    Thread.currentThread().sleep(1000);
			return "sleep on";
		}else{
			return sleep;
		}
	}

驗證方法:分別傳送兩個請求,

第一個請求:http://localhost:8080/a/admin/system/example/sleep?sleep=on

第二個請求:http://localhost:8080/a/admin/system/example/sleep?sleep=test

驗證結果:第一個請求發出去以後,本地伺服器等待100s,然後返回結果"sleep on",在本地伺服器等待的者100s當中,傳送第二個請求,直接返回結果"test"。說明之間的執行緒是不互相影響的。

為什麼SpringMVC 要設計成單例模式的呢?

   Spring的Bean作用域有5個:   

        1.  singleton  單例模式,當spring建立applicationContext的時候,spring會初始化所有的該作用域例項,加上lazy-init就可以避免

        2.  prototype  原型模式,每次通過getBean獲取該Bean就會產生一個例項,建立之後spring將不再對其進行管理

        3.  request    每次請求都會產生一個請求域,spring依舊對其監聽

        4.  session   同上

        5. global Session 全域性web域,類似servlet的application

1、為了效能。 

2、不需要多例(上面的demo已經說明,在使用單例的時候,會改變其設定的變數)

最佳實踐:

1、不要在controller中定義成員變數。(在工作中的程式設定,是絕對不允許的)

2、萬一必須要定義一個非靜態成員變數時候,則通過註解@Scope("prototype"),將其設定為多例模式。