深入理解springMVC中的Model和Session屬性
作為一個javaweb應(yīng)用的開發(fā)者,你快速學(xué)習(xí)了request(HttpRequest)和Session(HttpSession)的范圍,理解這些范圍并且在這些范圍內(nèi)數(shù)據(jù)和對象是如何是進(jìn)出的對設(shè)計和構(gòu)建web應(yīng)用是非常關(guān)鍵的。
springMVC的范圍
當(dāng)我用springMVC寫web應(yīng)用的時候,我發(fā)現(xiàn)spring model和session有一點神秘—特別是與http reques、和session范圍關(guān)聯(lián)起來這些我都已經(jīng)了解了。
spring的model元素會在我的session或者request中找到嗎,如果是這樣,我怎么控制這個過程呢,在這篇文章中我希望能夠解密springMVC的model和session是如何工作的。
spring的@MODELATTRIBUTE
這里有好幾種向spring的Model添加數(shù)據(jù)的方式。數(shù)據(jù)或者對象通常通過在controller上的注釋方法添加到spring中的model中去。
下邊這個例子中,@ModelAttribute用來將MyCommandBean的實例以key值為myRequestObject添加到model中去
@Controller
public class MyController {
@ModelAttribute("myRequestObject")
public MyCommandBean addStuffToRequestScope() {
System.out.println("Inside of addStuffToRequestScope");
MyCommandBean bean = new MyCommandBean("Hello World",42);
return bean;
}
@RequestMapping("/dosomething")
public String requestHandlingMethod(Model model, HttpServletRequest request) {
System.out.println("Inside of dosomething handler method");
System.out.println("--- Model data ---");
Map modelMap = model.asMap();
for (Object modelKey : modelMap.keySet()) {
Object modelValue = modelMap.get(modelKey);
System.out.println(modelKey + " -- " + modelValue);
}
System.out.println("=== Request data ===");
java.util.Enumeration reqEnum = request.getAttributeNames();
while (reqEnum.hasMoreElements()) {
String s = reqEnum.nextElement();
System.out.println(s);
System.out.println("==" + request.getAttribute(s));
}
return "nextpage";
}
// ... the rest of the controller
}
在一個請求的request中,任何使用@ModelAttribute注解的方法會在controller的handler方法(像上邊例子匯總的requestHandlingMethod 方法)之前被調(diào)用。
在這些handler方法執(zhí)行前,這些方法把數(shù)據(jù)增加到j(luò)ava.util.map中最終添加到spring Model中去。這可以通過一個簡單的實驗證明,我創(chuàng)建了兩個jsp頁面:index.jsp和nextpage.jsp。
index.jsp中的鏈接用來發(fā)送request到web應(yīng)用中來觸發(fā)Mycontroller中的requestHandlingMethod()方法。requestHandlingMethod()方法返回“nextpage”作為下一個視圖邏輯上的名字,在這個例子中我們解析為nextpage.jsp。
當(dāng)這個小的web站點用這種方式執(zhí)行的時候,controller里邊的System.out.println方法表明了@ModelAttribute方法是如何在handler方法之前運行的。
它同樣也展示了這個MyCommandBean被創(chuàng)建和添加到springModel中去的過程。
Inside of addStuffToRequestScope Inside of dosomething handler method --- Model data --- myRequestObject -- MyCommandBean [someString=Hello World, someNumber=42] === Request data === org.springframework.web.servlet.DispatcherServlet.THEME_SOURCE ==WebApplicationContext for namespace 'dispatcher-servlet': startup date [Sun Oct 13 21:40:56 CDT 2013]; root of context hierarchy org.springframework.web.servlet.DispatcherServlet.THEME_RESOLVER ==org.springframework.web.servlet.theme.FixedThemeResolver@204af48c org.springframework.web.servlet.DispatcherServlet.CONTEXT ==WebApplicationContext for namespace 'dispatcher-servlet': startup date [Sun Oct 13 21:40:56 CDT 2013]; root of context hierarchy org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping ==dosomething.request org.springframework.web.servlet.HandlerMapping.bestMatchingPattern ==/dosomething.* org.springframework.web.servlet.DispatcherServlet.LOCALE_RESOLVER ==org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@18fd23e4
現(xiàn)在的問題是“springModel數(shù)據(jù)存儲在哪?”它存儲在標(biāo)準(zhǔn)的java request范圍中嗎?答案是“是的”,從上邊的輸出可以看出,當(dāng)handler方法執(zhí)行的時候MyCommandBean是在model中,但是沒有在request對象中。
確實,spring不會把model數(shù)據(jù)作為request的屬性,直到執(zhí)行handler方法之后和下一個視圖之前(在這個例子中是nextpage.jsp)

這也可以通過打印存儲在index.jsp和nextpage.jsp中的HttpServletRequest中的數(shù)據(jù)展示出來,這兩個頁面我都使用jsp來展示HttpServletRequest的屬性
Request Scope (key==values)
<%
java.util.Enumeration<String> reqEnum = request.getAttributeNames();
while (reqEnum.hasMoreElements()) {
String s = reqEnum.nextElement();
out.print(s);
out.println("==" + request.getAttribute(s));
%><br />
<%
}
%>
當(dāng)應(yīng)用打開并且index.jsp展現(xiàn)的時候,你可以看到在Request范圍內(nèi)沒有屬性
do something Request Scope(key==values)
在這個例子中,當(dāng)“do something”連接被點擊的時候觸發(fā)了MyController的handler方法執(zhí)行,繼而導(dǎo)致nextpage.jsp被執(zhí)行,考慮到這是同樣的jsp內(nèi)容,再次提出不免有些啰嗦。當(dāng)nextpage.jsp提出的時候,表明model的MyCommandBean在controller里被創(chuàng)建,并已經(jīng)被加到HttpServletRequest范圍中去了。這個spring model的屬性key已經(jīng)被復(fù)制,并且當(dāng)做Request屬性的key。
所以spring model數(shù)據(jù)的創(chuàng)建要早于handler方法的執(zhí)行,在下一個視圖加載前就已經(jīng)被復(fù)制到HttpServletRequest中去了。
spring Model和Request后邊的原因
你可能會疑惑為什么spring使用model屬性,為什么不直接將數(shù)據(jù)加到Request對象中去呢,在Rod Johnson的書中我找到了這個問題的答案。下邊就是來自這本書的關(guān)于model元素的文本。
直接給HttpServletRequest(或者Request屬性)增加元素看起來實現(xiàn)了相同的目的,做這件事的原因很明確,當(dāng)我們查看我們?yōu)镸VC框架設(shè)置的要求的時候,應(yīng)該盡可能使視圖無關(guān)的,意味著視圖技術(shù)不和HttpServletRequest綁定。
Spring的@SESSIONATTRIBUTE
現(xiàn)在你知道spring的model數(shù)據(jù)是如何管理的并且是如何和Httprequset屬性關(guān)聯(lián)的,那么spring的session數(shù)據(jù)呢?
spring的@SessionAttributes在controller上使用指定model屬性應(yīng)該存儲在Session中。實際上,精確的講spring開發(fā)文檔已經(jīng)表明了@SessionAttributes注解“列出了應(yīng)該存儲在Session中或者對話存儲中model屬性的名字”。
實際上,@SessionAttribute允許你做的是在加載視圖之前,告訴spring你的哪一個model Attributes將會被復(fù)制到httpSession中去。
Session Scope (key==values)
<%
java.util.Enumeration<String> sessEnum = request.getSession()
.getAttributeNames();
while (sessEnum.hasMoreElements()) {
String s = sessEnum.nextElement();
out.print(s);
out.println("==" + request.getSession().getAttribute(s));
%><br />
<%
}
%>
我在MyController中用@SessionAttributes做注解,來把同樣的model屬性加到spring Session中去。
@Controller
@SessionAttributes("myRequestObject")
public class MyController {
...
}
我同樣也在handler方法中增加了代碼來展示什么屬性在httpSession中
@SuppressWarnings("rawtypes")
@RequestMapping("/dosomething")
public String requestHandlingMethod(Model model, HttpServletRequest request, HttpSession session) {
System.out.println("Inside of dosomething handler method");
System.out.println("--- Model data ---");
Map modelMap = model.asMap();
for (Object modelKey : modelMap.keySet()) {
Object modelValue = modelMap.get(modelKey);
System.out.println(modelKey + " -- " + modelValue);
}
System.out.println("=== Request data ===");
java.util.Enumeration<String> reqEnum = request.getAttributeNames();
while (reqEnum.hasMoreElements()) {
String s = reqEnum.nextElement();
System.out.println(s);
System.out.println("==" + request.getAttribute(s));
}
System.out.println("*** Session data ***");
Enumeration<String> e = session.getAttributeNames();
while (e.hasMoreElements()){
String s = e.nextElement();
System.out.println(s);
System.out.println("**" + session.getAttribute(s));
}
return "nextpage";
}
現(xiàn)在,當(dāng)我們使用@SessionAttributes注解后我們可以看到什么在session對象中,包括springMVC處理一個http 請求的前中后的過程里。結(jié)果如下,首先是index.jsp展示,我們可以看到在HttpServletRequest和httpSession中都沒有屬性數(shù)據(jù)。
do something Request Scope(key == values) Session Scope(key == values)
在handler方法執(zhí)行過程中(HttpServletRequest),你可以看到MyCommandBean被加入到spring model屬性中去,但是沒有在HttpServletRequest和HttpSession范圍中。
但是在handler方法執(zhí)行之后并且nextpage.jsp被加載,你可以看到model的屬性數(shù)據(jù)確實被作為屬性復(fù)制到HttpServletRequest和httpSession中。
控制對話屬性
現(xiàn)在你已經(jīng)對spring model和Session屬性數(shù)據(jù)是如何加載到HttpServletReq。uest和httpSession有很好的理解了。但你依舊對管理spring Session中的數(shù)據(jù)心生疑惑。spring提供了移除spring Session屬性的方式,也可以在HttpSession中移除。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring boot集成swagger2生成接口文檔的全過程
這篇文章主要給大家介紹了關(guān)于Spring boot集成swagger2生成接口文檔的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Spring boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Spring Data JPA 復(fù)雜/多條件組合分頁查詢
本文主要介紹了Spring Data JPA 復(fù)雜/多條件組合分頁查詢的相關(guān)資料。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04
SpringBoot中使用Redisson的實現(xiàn)示例
Redission是一個強(qiáng)大的Java庫,用于構(gòu)建和管理分布式系統(tǒng)中的緩存和任務(wù)調(diào)度,本文主要介紹了SpringBoot中使用Redisson的實現(xiàn)示例,感興趣的可以了解一下2023-12-12

