上回說到, spring組件的註解Scope大約有singleton、prototype、request、session、global session 這麼幾種經常使用的場景。這裏須要特別說明一下,根據源代碼顯示 Scope註解分爲ConfigurableBeanFactory和WebApplicationContext兩個大類,ConfigurableBeanFactory包含(singleton、prototype)兩種Scope,WebApplicationContext下面有(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,request,session,application,servletContext,contextParameters,contextAttributes)這麼幾種Scope。今天的示例重點是對request,session兩個場景進行一次測試。html
那在談到具體的示例前,我先分享下對這兩種場景的使用心得,以便與各位看官進行思想上的神交! 咱們都知道B/S站點運行起來後,是一個多線程的運行環境。每一個客戶端登陸都會產生一個session會話,它的生命週期 從登陸系統到 session過時,期間session上存儲的信息都是有效可用的,我習慣於叫它會話級的緩存,像用戶登陸的身份信息咱們通常都會綁定到這個session上。這裏咱們要講的@Scope("session"),就是spring提供出來的一個會話級bean方案,在這種模式下,用spring的DI功能來獲取組件,能夠作到在會話的生命週期中這個組件只有一個實例。接下來再說請求(request),http協議的處理模型,從客戶端發起request請求,到服務端的處理,最後response給客戶端,咱們稱爲一次完整的請求。在這樣的一次請求過程當中,咱們的服務站可能要串行調用funcA->funcB->funcC·... 這樣的一串函數,假如咱們的系統須要作細緻的權限校驗(用戶權限,數據權限),更可怕的是funcA,funcB,funcC是3我的分別實現的,並且都要作權限校驗。那麼極有可能會出現3我的各鏈接了一次數據庫,讀取了同一批權限數據。這裏想象一下,假如一個數據讀取要花2秒,那麼3個方法就要花費6秒的處理時間。但實際上這些數據只用在這個請求過程當中讀取一次,緩存在request上下文環境中,我習慣稱之爲線程級緩存。關於線程級緩存java有ThreadLocal方案,像Hibernate的事務就是用這種方案維持一次執行過程當中數據庫鏈接的惟一。固然,今天要講的@Scope("request")也能夠作到這種線程級別的緩存。下面咱們看看具體的測試示例java
實現測試步驟說明:spring
一、建立一個@Scope("session")標註的Bean組件。關於proxyMode這個參數,是爲了解決依存的會話或者請求上下文環境尚未時,自動裝載組件報錯,這裏交給JDK代理,能夠保證環境準備就緒時再執行組件裝載。數據庫
@Component //@Scope(value=WebApplicationContext.SCOPE_SESSION) @Scope(value=WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.INTERFACES) public class SessionBean implements ISessionBean { private UUID uuid; public SessionBean(){ uuid = UUID.randomUUID(); } public void printId(){ System.out.println("SessionBean:"+uuid); } }
二、建立一個@Scope("request")標註的Bean組件緩存
@Component @Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode= ScopedProxyMode.INTERFACES) public class RequestBean implements IRequestBean { private UUID uuid; public RequestBean() { uuid = UUID.randomUUID(); } public void printId() { System.out.println("RequestBean:" + uuid); } }
三、添加一個多例的組件自動組裝服務類,方便獲取session、request組件。session
@Service @Scope(value=ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class BeanInstance { @Autowired private IRequestBean requestBean; @Autowired private ISessionBean sessionBean; public IRequestBean getRequestBean() { return requestBean; } public ISessionBean getSessionBean() { return sessionBean; } }
四、添加測試代碼多線程
@Controller @RequestMapping("/user") // 添加session信息的註解,能夠實現 session信息與map的映射 賦值 @SessionAttributes("user") public class UserController { @Autowired private BeanInstance beanInstance1; @Autowired private BeanInstance beanInstance2; @RequestMapping(value = "/login", method = RequestMethod.GET) public String login(String name, Model model, HttpServletRequest request, HttpSession session) { model.addAttribute("user", name); System.out.println("SessionBean-1"); beanInstance1.getSessionBean().printId(); System.out.println("SessionBean-2"); beanInstance2.getSessionBean().printId(); System.out.println("RequestBean-1"); beanInstance1.getRequestBean().printId(); System.out.println("RequestBean-2"); beanInstance2.getRequestBean().printId(); return "user/check"; } /** * 檢查自動裝載的信息 * @param model * @param request * @param session * @return */ @RequestMapping(value = "/check", method = RequestMethod.GET) public String check(Model model, HttpServletRequest request, HttpSession session) { System.out.println("SessionBean-1"); beanInstance1.getSessionBean().printId(); System.out.println("SessionBean-2"); beanInstance2.getSessionBean().printId(); System.out.println("RequestBean-1"); beanInstance1.getRequestBean().printId(); System.out.println("RequestBean-2"); beanInstance2.getRequestBean().printId(); return "user/check"; } }
五、先發起一次登陸請求,再發起一次登陸後的檢查請求。從打印的測試結果能夠看到兩次請求 拿到的session組件,其對應的ID都是相同的,因此在同一個會話中,session組件是惟一的。而request組件,在同一個request請求過程當中,調用兩次都獲得同一個組件ID,而在第二次請求中request組件的ID改變了。所以正如上面所說,@Scope("request")模式在同一請求過程當中,spring返回的組件也是惟一的,咱們能夠用這個方案來作線程級別的數據緩存。app
參考資料dom
http://www.javashuo.com/article/p-cdfolfba-du.html
http://www.javashuo.com/article/p-zyjkencn-eb.html函數