當前位置:
首頁 >
前端技术
> javascript
>内容正文
javascript
Spring mvc 原理浅析
生活随笔
收集整理的這篇文章主要介紹了
Spring mvc 原理浅析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Spring MVC主要包括以下要點:
1:由DispatcherServlet控制的整個流程;
2:注解驅動的控制器,其中包括請求映射、數據的綁定和格式化;
3:文件上傳;
4:一些雜項,如靜態資源處理、異常處理等等。
這些東西構成了精致的Spring MVC框架,下面我將針對這些要點做詳細討論,以期其能在開發上對各位觀眾有些作用。
1.? ?Spring MVC框架原理
DispatcherServlet是Spring MVC的靈魂和心臟,它負責接收HTTP請求并協調Spring MVC的各個組件完成請求處理的工作。和任何Servlet一樣,用戶必須在web.xml中配置好DispatcherServlet,并且讓其接受一 切HTTP請求。當用戶的請求被截獲時,DispatcherServlet通過HandlerMapping定位到特定的Controller(使用 @Controller注解的普通Java類,此處已經定位到了具體的業務處理方法了,所以我們稱其為Handler)。然后通過 HandlerAdapter調用Handler中對應的業務處理方法(從這里可以看出與Struts不同的是,Spring MVC是方法級的攔截)。業務處理方法返回一個邏輯視圖名(View)和模型數據(Model,二者統稱ModelAndView)交給 DispatcherServlet,DispatcherServlet調用ViewResolver解析出真實的視圖對象,得到這個視圖對象 后,DispatcherServlet就使用Model對其進行渲染,將最后結果返回給用戶。 要了解Spring MVC框架的工作原理,必須回答以下三個問題: 1)??DispatcherServlet如何截獲特定的HTTP請求,交由Spring MVC框架處理? 2)??位于Web層的Spring容器(WebApplicationContext),如何與位于業務層的Spring容器(ApplicationContext)建立關聯,以使Web層的Bean可以調用業務層的Bean? 3)??如何初始化SpringMVC的各個組件,并將它們裝配到DispatcherServlet中? 第一個問題已然在上面的步驟中說明了,很簡單。對于第二條,其實Web層的容器是作為業務層容器的子容器來配置的,所以訪問不是問題。第三,DispatcherServlet有一個初始化方法initStrategies,它在WebApplicationContext初始化后執行,此時所有的組件Bean均已可用。該方法通過反射機制查找并裝配Spring容器中用戶顯示自定義的組件Bean,如果找不到再裝配默認的組件實例。 怎么樣,是不是對SpringMVC的工作原理有一個模糊的認識了。到這一步你只要知道Spring MVC也是基于Servlet的,它可以根據URL直接定位到業務處理方法,同時我們可以天然地使用Spring容器,相當之美! 2.? ?注解驅動的控制器 正如上面所說,SpringMVC可以直接定位到業務處理方法,那么我們提交的數據是不是還要像Servlet那樣通過HTTPServlet來獲取,或 者可以像Struts一樣綁定到Form中或是Action里面。當然可以,不僅僅如此,Spring MVC還提供了更加強大的數據綁定和轉化的功能,使之將Struts之類遠遠甩到后面去了。 2.1.?請求映射 在POJO類定義處標注@Controller,再通過<context:component-scan/>掃描相應的類包,即可使POJO 成為一個能處理HTTP請求的控制器。一個控制器的每一個方法都可以成為請求處理方法,如何將請求映射到控制器的方法中是Spring MVC框架最重要的任務之一,這項任務由@RequestMapping承擔。
<ignore_js_op style="word-wrap: break-word;"> ①處的注解很重要,Spring會在啟動的時候掃描它,將其劃入到Handler中去,沒有它一切都白搭。②處的@RequestMapping標注的路 徑是相對于應用系統根路徑的,在此處寫這個注解是為了同一控制器的多個處理方法負責處理相同業務模塊的不同操作,這個注解亦可省略,但建議不要這樣。③處 的注解是必須的,要定位到具體的處理方法中去。上面的顯示列表的URL可以是這樣:host:port/app/excavation /list.XXX。 @RequestMapping不但支持標準的URL,還支持Ant風格(即?、*和**的字符)的和帶{xxx}占位符的URL。以下URL都是合法的:/user/*/createUser ? ?匹配/user/aaa/createUser、/user/bbb/createUser等URL。 /user/**/createUser ? ?匹配/user/createUser、/user/aaa/bbb/createUser等URL。 /user/createUser?? ? ?匹配/user/createUseraa、/user/createUserbb等URL。 /user/{userId} ? ?匹配user/123、user/abc等URL。 /user/**/{userId} ? ?匹配user/aaa/bbb/123、user/aaa/456等URL。 company/{companyId}/user/{userId}/detail ? ?匹配company/123/user/456/detail等的URL。 復制代碼
占位符的URL是Spring 3.0新增的功能,該功能在SpringMVC向REST目標挺進的發展過程中具有里程碑的意義。通過@PathVariable可以將URL中的占位符參數綁定到控制器處理方法的入參中,如下所示:
<ignore_js_op style="word-wrap: break-word;"> 除了通過URL進行映射外,我們還可以通過請求參數、請求方法和請求頭進行映射,由于以上方法已經足夠我們進行通常的開發,所以這里就不再詳述其他映射方法了,有興趣的同事可以查詢互聯網。 2.2.?數據的綁定 前面說過了,SpringMVC是方法級的映射,那么Spring是如何處理方法簽名的,又是如何將表單數據綁定到方法參數中的?下面我們就來討論這個問題。 2.2.1.?處理方法簽名 首先,我們可以在方法簽名中放入@CookieValue注解參數,Spring自動將Cookie值綁定到參數中;同理@RequestHeader可 以綁定報文頭的屬性值;同時我們還可以將Servlet API如HttpServletRequest、HttpServletResponse、HttpSession、WebRequest直接作為方法參 數,Spring負責綁定;Spring MVC還允許控制器的處理方法使用java.io.InputStream/java.io.Reader及java.io.OutputStream /java.io.Writer作為方法的入參,SpringMVC將獲取ServletRequest的InputStream/Reader或 ServletResponse的OutputStream/Writer,然后按類型匹配的方式,傳遞給控制器的處理方法入參;控制器處理方法的入參除 支持以上類型的參數以外,還支持java.util.Locale、java.security.Principal,可以通過Servlet的 HttpServletRequest的getLocale()及getUserPrincipal()得到相應的值。如果處理方法的入參類型為 Locale或Principal,Spring MVC自動從請求對象中獲取相應的對象并傳遞給處理方法的入參。 2.2.1.?表單數據綁定到方法參數 再有,表單的數據只要名稱相同就可以往方法參數中放,或者是級聯的可以封裝成對象置于參數中,Spring會自動綁定,如下圖所示,極其地方便簡單:
2.2.3. HttpMessageConverter<T> 最后,還有一類處理方法入參的形式,即使用HttpMessageConverter<T>,這個非常強大。它提供了兩種途徑: 1)? ?? ???使用@RequestBody/@ResponseBody對處理方法進行標注; 2)? ?? ???使用HttpEntity<T>/ResponseEntity<T>作為處理方法的入參或返回值。 HttpMessageConverter顧名思義,它負責將請求信息轉換為一個對象,或者將對象輸出為響應信息。前面說過,當請求映射到具體的處理方法后,DispatcherServlet調用HandlerAdapter來封裝并執行處理方法。DispatcherServlet默認已經安裝了AnnotationMethodHandlerAdapter作為HandlerAdapter的組件實現類,HttpMessageConverter即由AnnotationMethodHandlerAdapter使用,將請求信息轉換為對象,或者將對象轉換為響應信息。先看幾個示例:將報文體轉換為字符串綁定到方法入參中 @RequestMapping(value= "/handle41") publicString handle41(@RequestBody??StringrequestBody ) { ? ?? ?System.out.println(requestBody); ? ?? ?return "success"; } 復制代碼
讀取一張圖片,并將圖片數據輸出到響應流中,客戶端將顯示這張圖片 @ResponseBody @RequestMapping(value= "/handle42/{imageId}") public byte[]handle42(@PathVariable("imageId") String imageId) throwsIOException { ? ?? ? System.out.println("load image of"+imageId); ? ?? ? Resource res = newClassPathResource("/image.jpg"); ? ?? ? byte[] fileData=FileCopyUtils.copyToByteArray(res.getInputStream()); ? ?? ? return fileData; } 復制代碼
@RequestMapping(value= "/handle43") publicString handle43(HttpEntity<String> httpEntity){ ? ???long contentLen = httpEntity.getHeaders().getContentLength(); ? ???System.out.println(httpEntity.getBody()); ? ?? ?return "success"; } @RequestMapping(params= "method=login") public ResponseEntity<String>doFirst(){ ? ? HttpHeaders headers = new HttpHeaders(); ? ? MediaType mt=newMediaType("text","html",Charset.forName(“UTF-8")); ? ? headers.setContentType(mt); ? ? ResponseEntity<String> re=null; ? ? String return = newString("test"); ? ? re=newResponseEntity<String>(return,headers, HttpStatus.OK); ? ? return re; } 復制代碼
這里講一下HttpMessageConverter中的重點@ResponseBody,我們用它來處理XML和JSON非常之方便。只要在 SpringWeb容器中為AnnotationMethodHandlerAdapter裝配好相應的處理XML、JSON的 HttpMessageConverter(AnnotationMethodHandlerAdapter默認只裝配部分轉換器),并在交互中通過請求 的Accept指定MIME類型,Spring MVC就可以使服務端的處理方法和客戶端透明地通過XML或JSON格式的消息進行通信了。
<ignore_js_op style="word-wrap: break-word;"> 代碼中我們可以這樣做:
-wrap:? ? ?? ? 這部分其實很簡單,對于服務端的處理方法而言,除使用@RequestBody/@ResponseBody或HttpEntity<T>/ResponseEntity<T>進行方法簽名外,不需要進行任何額外的處理,借由Spring MVC中裝配的HttpMessageConverter,它即擁有了處理XML及JSON的能力了。 3.? ?文件上傳 Spring MVC為文件上傳提供了直接的支持,這種支持是通過即插即用的MultipartResolver實現的。
<ignore_js_op style="word-wrap: break-word;"> ? ?? ? 為了能使CommonsMultipartResolver正確工作,必須將JakartaCommons FileUpload和Jakarta Commons io的類包添加到類路徑下。下面是代碼的寫法:
<ignore_js_op style="word-wrap: break-word;"> file:///C:\Users\wmq\AppData\Local\Temp\msohtmlclip1\01\clip_image015.jpg 4.? ?雜項 這里主要講一下靜態文件的處理。如何訪問到靜態的文件,如jpg,js,css?如果你的DispatcherServlet攔截 *.do這樣的URL,就不存在訪問不到靜態資源的問題。如果你的DispatcherServlet攔截“/”,攔截了所有的請求,同時 對*.js,*.jpg的訪問也就被攔截了。這種情況下如何搞定靜態文件訪問問題: 方案一:激活Tomcat的defaultServlet來處理靜態文件<servlet-mapping> ? ?? ?? ?? ?<servlet-name>default</servlet-name> ? ?? ?? ?? ?<url-pattern>*.jpg</url-pattern> ? ?? ?? ?</servlet-mapping> ? ?? ?? ?<servlet-mapping> ? ?? ?? ?? ?<servlet-name>default</servlet-name> ? ?? ?? ?? ?<url-pattern>*.js</url-pattern> ? ?? ?? ?</servlet-mapping> ? ?? ?? ?<servlet-mapping> ? ?? ?? ?? ?<servlet-name>default</servlet-name> ? ?? ?? ?? ?<url-pattern>*.css</url-pattern> ? ?? ?? ?</servlet-mapping> 復制代碼
? ? 要配置多個,每種文件配置一個;要寫在DispatcherServlet的前面,讓defaultServlet先攔截,這個就不會進入Spring了,我想性能是最好的吧。各服務器defaultServlet名稱如下:
Tomcat,Jetty, JBoss, and GlassFish??默認 Servlet的名字 --"default" GoogleApp Engine 默認 Servlet的名字 -- "_ah_default" Resin 默認 Servlet的名字 --"resin-file" WebLogic 默認 Servlet的名字??-- "FileServlet" WebSphere??默認 Servlet的名字 --"SimpleFileServlet" 復制代碼
方案二:在spring3.0.4以后版本提供了mvc:resources
<!-- 對靜態資源文件的訪問--> <mvc:resourcesmapping="/images/**" location="/images/" /> 復制代碼
? ??/images/**映射到ResourceHttpRequestHandler進行處理,location指定靜態資源的位置.可以是webapplication根目錄下、jar包里面,這樣可以把靜態資源壓縮到jar包中。使用<mvc:resources/>元素,把mapping的URI注冊到SimpleUrlHandlerMapping的urlMap中,key為mapping的URI pattern值,而value為ResourceHttpRequestHandler,這樣就巧妙的把對靜態資源的訪問由HandlerMapping轉到ResourceHttpRequestHandler處理并返回,所以就支持classpath目錄,jar包內靜態資源的訪問.另外需要注意的一點是,不要對SimpleUrlHandlerMapping設置defaultHandler.因為對static uri的defaultHandler就是ResourceHttpRequestHandler,否則無法處理static resourcesrequest.
方案三,使用<mvc:default-servlet-handler/>
<mvc:default-servlet-handler/> 復制代碼
? ??會把"/**" url,注冊到SimpleUrlHandlerMapping的urlMap中,把對靜態資源的訪問由HandlerMapping轉到DefaultServletHttpRequestHandler處理并返回。DefaultServletHttpRequestHandler使用就是各個Servlet容器自己的默認Servlet。
補充說明:多個HandlerMapping的執行順序問題:
DefaultAnnotationHandlerMapping的order屬性值是:0 <mvc:resources/>自動注冊的 SimpleUrlHandlerMapping的order屬性值是:2147483646 <mvc:default-servlet-handler/>自動注冊 的SimpleUrlHandlerMapping的order屬性值是: 2147483647 復制代碼
? ??Spring會先執行order值比較小的。當訪問一個a.jpg圖片文件時,先通過DefaultAnnotationHandlerMapping來找處理器,一定是找不到的,我們沒有叫a.jpg的Action。再按order值升序找,由于最后一個SimpleUrlHandlerMapping是匹配"/**"的,所以一定會匹配上,再響應圖片。
? ??最后再說明一下,如果你的DispatcherServlet攔截?*.do這樣的URL,就不存上述問題了。
DispatcherServlet是Spring MVC的靈魂和心臟,它負責接收HTTP請求并協調Spring MVC的各個組件完成請求處理的工作。和任何Servlet一樣,用戶必須在web.xml中配置好DispatcherServlet,并且讓其接受一 切HTTP請求。當用戶的請求被截獲時,DispatcherServlet通過HandlerMapping定位到特定的Controller(使用 @Controller注解的普通Java類,此處已經定位到了具體的業務處理方法了,所以我們稱其為Handler)。然后通過 HandlerAdapter調用Handler中對應的業務處理方法(從這里可以看出與Struts不同的是,Spring MVC是方法級的攔截)。業務處理方法返回一個邏輯視圖名(View)和模型數據(Model,二者統稱ModelAndView)交給 DispatcherServlet,DispatcherServlet調用ViewResolver解析出真實的視圖對象,得到這個視圖對象 后,DispatcherServlet就使用Model對其進行渲染,將最后結果返回給用戶。 要了解Spring MVC框架的工作原理,必須回答以下三個問題: 1)??DispatcherServlet如何截獲特定的HTTP請求,交由Spring MVC框架處理? 2)??位于Web層的Spring容器(WebApplicationContext),如何與位于業務層的Spring容器(ApplicationContext)建立關聯,以使Web層的Bean可以調用業務層的Bean? 3)??如何初始化SpringMVC的各個組件,并將它們裝配到DispatcherServlet中? 第一個問題已然在上面的步驟中說明了,很簡單。對于第二條,其實Web層的容器是作為業務層容器的子容器來配置的,所以訪問不是問題。第三,DispatcherServlet有一個初始化方法initStrategies,它在WebApplicationContext初始化后執行,此時所有的組件Bean均已可用。該方法通過反射機制查找并裝配Spring容器中用戶顯示自定義的組件Bean,如果找不到再裝配默認的組件實例。 怎么樣,是不是對SpringMVC的工作原理有一個模糊的認識了。到這一步你只要知道Spring MVC也是基于Servlet的,它可以根據URL直接定位到業務處理方法,同時我們可以天然地使用Spring容器,相當之美! 2.? ?注解驅動的控制器 正如上面所說,SpringMVC可以直接定位到業務處理方法,那么我們提交的數據是不是還要像Servlet那樣通過HTTPServlet來獲取,或 者可以像Struts一樣綁定到Form中或是Action里面。當然可以,不僅僅如此,Spring MVC還提供了更加強大的數據綁定和轉化的功能,使之將Struts之類遠遠甩到后面去了。 2.1.?請求映射 在POJO類定義處標注@Controller,再通過<context:component-scan/>掃描相應的類包,即可使POJO 成為一個能處理HTTP請求的控制器。一個控制器的每一個方法都可以成為請求處理方法,如何將請求映射到控制器的方法中是Spring MVC框架最重要的任務之一,這項任務由@RequestMapping承擔。
<ignore_js_op style="word-wrap: break-word;"> ①處的注解很重要,Spring會在啟動的時候掃描它,將其劃入到Handler中去,沒有它一切都白搭。②處的@RequestMapping標注的路 徑是相對于應用系統根路徑的,在此處寫這個注解是為了同一控制器的多個處理方法負責處理相同業務模塊的不同操作,這個注解亦可省略,但建議不要這樣。③處 的注解是必須的,要定位到具體的處理方法中去。上面的顯示列表的URL可以是這樣:host:port/app/excavation /list.XXX。 @RequestMapping不但支持標準的URL,還支持Ant風格(即?、*和**的字符)的和帶{xxx}占位符的URL。以下URL都是合法的:
<ignore_js_op style="word-wrap: break-word;"> 除了通過URL進行映射外,我們還可以通過請求參數、請求方法和請求頭進行映射,由于以上方法已經足夠我們進行通常的開發,所以這里就不再詳述其他映射方法了,有興趣的同事可以查詢互聯網。 2.2.?數據的綁定 前面說過了,SpringMVC是方法級的映射,那么Spring是如何處理方法簽名的,又是如何將表單數據綁定到方法參數中的?下面我們就來討論這個問題。 2.2.1.?處理方法簽名 首先,我們可以在方法簽名中放入@CookieValue注解參數,Spring自動將Cookie值綁定到參數中;同理@RequestHeader可 以綁定報文頭的屬性值;同時我們還可以將Servlet API如HttpServletRequest、HttpServletResponse、HttpSession、WebRequest直接作為方法參 數,Spring負責綁定;Spring MVC還允許控制器的處理方法使用java.io.InputStream/java.io.Reader及java.io.OutputStream /java.io.Writer作為方法的入參,SpringMVC將獲取ServletRequest的InputStream/Reader或 ServletResponse的OutputStream/Writer,然后按類型匹配的方式,傳遞給控制器的處理方法入參;控制器處理方法的入參除 支持以上類型的參數以外,還支持java.util.Locale、java.security.Principal,可以通過Servlet的 HttpServletRequest的getLocale()及getUserPrincipal()得到相應的值。如果處理方法的入參類型為 Locale或Principal,Spring MVC自動從請求對象中獲取相應的對象并傳遞給處理方法的入參。 2.2.1.?表單數據綁定到方法參數 再有,表單的數據只要名稱相同就可以往方法參數中放,或者是級聯的可以封裝成對象置于參數中,Spring會自動綁定,如下圖所示,極其地方便簡單:
2.2.3. HttpMessageConverter<T> 最后,還有一類處理方法入參的形式,即使用HttpMessageConverter<T>,這個非常強大。它提供了兩種途徑: 1)? ?? ???使用@RequestBody/@ResponseBody對處理方法進行標注; 2)? ?? ???使用HttpEntity<T>/ResponseEntity<T>作為處理方法的入參或返回值。 HttpMessageConverter顧名思義,它負責將請求信息轉換為一個對象,或者將對象輸出為響應信息。前面說過,當請求映射到具體的處理方法后,DispatcherServlet調用HandlerAdapter來封裝并執行處理方法。DispatcherServlet默認已經安裝了AnnotationMethodHandlerAdapter作為HandlerAdapter的組件實現類,HttpMessageConverter即由AnnotationMethodHandlerAdapter使用,將請求信息轉換為對象,或者將對象轉換為響應信息。先看幾個示例:
<ignore_js_op style="word-wrap: break-word;"> 代碼中我們可以這樣做:
-wrap:? ? ?? ? 這部分其實很簡單,對于服務端的處理方法而言,除使用@RequestBody/@ResponseBody或HttpEntity<T>/ResponseEntity<T>進行方法簽名外,不需要進行任何額外的處理,借由Spring MVC中裝配的HttpMessageConverter,它即擁有了處理XML及JSON的能力了。 3.? ?文件上傳 Spring MVC為文件上傳提供了直接的支持,這種支持是通過即插即用的MultipartResolver實現的。
<ignore_js_op style="word-wrap: break-word;"> ? ?? ? 為了能使CommonsMultipartResolver正確工作,必須將JakartaCommons FileUpload和Jakarta Commons io的類包添加到類路徑下。下面是代碼的寫法:
<ignore_js_op style="word-wrap: break-word;"> file:///C:\Users\wmq\AppData\Local\Temp\msohtmlclip1\01\clip_image015.jpg 4.? ?雜項 這里主要講一下靜態文件的處理。如何訪問到靜態的文件,如jpg,js,css?如果你的DispatcherServlet攔截 *.do這樣的URL,就不存在訪問不到靜態資源的問題。如果你的DispatcherServlet攔截“/”,攔截了所有的請求,同時 對*.js,*.jpg的訪問也就被攔截了。這種情況下如何搞定靜態文件訪問問題: 方案一:激活Tomcat的defaultServlet來處理靜態文件
總結
以上是生活随笔為你收集整理的Spring mvc 原理浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: css网络字体@font-face
- 下一篇: 【vps】教你写一个自己的随机图API