跟開濤學Spring MVC:第二章 Spring MVC入門

發表於2012-10-30

注:本文來自文章作者張開濤(@開濤)的投稿(原文)。伯樂線上也歡迎其他朋友投稿,如果您有新浪微博,請投稿時記得留下哦~

2.1、Spring Web MVC是什麼

Spring Web MVC是一種基於Java的實現了Web MVC設計模式的請求驅動型別的輕量級Web框架,即使用了MVC架構模式的思想,將web層進行職責解耦,基於請求驅動指的就是使用請求-響應模型,框架的目的就是幫助我們簡化開發,Spring Web MVC也是要簡化我們日常Web開發的。

另外還有一種基於元件的、事件驅動的Web框架在此就不介紹了,如Tapestry、JSF等。

Spring Web MVC也是服務到工作者模式的實現,但進行可優化。前端控制器是DispatcherServlet;應用控制器其實拆為處理器對映器(Handler Mapping)進行處理器管理和檢視解析器(View Resolver)進行檢視管理;頁面控制器/動作/處理器為Controller介面(僅包含ModelAndView handleRequest(request, response) 方法)的實現(也可以是任何的POJO類);支援本地化(Locale)解析、主題(Theme)解析及檔案上傳等;提供了非常靈活的資料驗證、格式化和資料繫結機制;提供了強大的約定大於配置(慣例優先原則)的契約式程式設計支援。

2.2、Spring Web MVC能幫我們做什麼

√讓我們能非常簡單的設計出乾淨的Web層和薄薄的Web層;

√進行更簡潔的Web層的開發;

√天生與Spring框架整合(如IoC容器、AOP等);

√提供強大的約定大於配置的契約式程式設計支援;

√能簡單的進行Web層的單元測試;

√支援靈活的URL到頁面控制器的對映;

√非常容易與其他檢視技術整合,如Velocity、FreeMarker等等,因為模型資料不放在特定的API裡,而是放在一個Model裡(Map資料結構實現,因此很容易被其他框架使用);

√非常靈活的資料驗證、格式化和資料繫結機制,能使用任何物件進行資料繫結,不必實現特定框架的API;

√提供一套強大的JSP標籤庫,簡化JSP開發;

√支援靈活的本地化、主題等解析;

√更加簡單的異常處理;

√對靜態資源的支援;

√支援Restful風格。

2.3、Spring Web MVC架構

Spring Web MVC框架也是一個基於請求驅動的Web框架,並且也使用了前端控制器模式來進行設計,再根據請求對映規則分發給相應的頁面控制器(動作/處理器)進行處理。首先讓我們整體看一下Spring Web MVC處理請求的流程:

2.3.1、Spring Web MVC處理請求的流程

如圖2-1

圖2-1

具體執行步驟如下:

1、  首先使用者傳送請求————>前端控制器,前端控制器根據請求資訊(如URL)來決定選擇哪一個頁面控制器進行處理並把請求委託給它,即以前的控制器的控制邏輯部分;圖2-1中的1、2步驟;

2、  頁面控制器接收到請求後,進行功能處理,首先需要收集和繫結請求引數到一個物件,這個物件在Spring Web MVC中叫命令物件,並進行驗證,然後將命令物件委託給業務物件進行處理;處理完畢後返回一個ModelAndView(模型資料和邏輯檢視名);圖2-1中的3、4、5步驟;

3、  前端控制器收回控制權,然後根據返回的邏輯檢視名,選擇相應的檢視進行渲染,並把模型資料傳入以便檢視渲染;圖2-1中的步驟6、7;

4、  前端控制器再次收回控制權,將響應返回給使用者,圖2-1中的步驟8;至此整個結束。

問題:

1、  請求如何給前端控制器?

2、  前端控制器如何根據請求資訊選擇頁面控制器進行功能處理?

3、  如何支援多種頁面控制器呢?

4、  如何頁面控制器如何使用業務物件?

5、  頁面控制器如何返回模型資料?

6、  前端控制器如何根據頁面控制器返回的邏輯檢視名選擇具體的檢視進行渲染?

7、  不同的檢視技術如何使用相應的模型資料?

首先我們知道有如上問題,那這些問題如何解決呢?請讓我們先繼續,在後邊依次回答。

 

2.3.2、Spring Web MVC架構

1、Spring Web MVC核心架構圖,如圖2-2

圖2-2

架構圖對應的DispatcherServlet核心程式碼如下:

核心架構的具體流程步驟如下:

1、  首先使用者傳送請求——>DispatcherServlet,前端控制器收到請求後自己不進行處理,而是委託給其他的解析器進行處理,作為統一訪問點,進行全域性的流程控制;

2、  DispatcherServlet——>HandlerMapping, HandlerMapping將會把請求對映為HandlerExecutionChain物件(包含一個Handler處理器(頁面控制器)物件、多個HandlerInterceptor攔截器)物件,通過這種策略模式,很容易新增新的對映策略;

3、  DispatcherServlet——>HandlerAdapter,HandlerAdapter將會把處理器包裝為介面卡,從而支援多種型別的處理器,即介面卡設計模式的應用,從而很容易支援很多型別的處理器;

4、  HandlerAdapter——>處理器功能處理方法的呼叫,HandlerAdapter將會根據適配的結果呼叫真正的處理器的功能處理方法,完成功能處理;並返回一個ModelAndView物件(包含模型資料、邏輯檢視名);

5、  ModelAndView的邏輯檢視名——> ViewResolver, ViewResolver將把邏輯檢視名解析為具體的View,通過這種策略模式,很容易更換其他檢視技術;

6、  View——>渲染,View會根據傳進來的Model模型資料進行渲染,此處的Model實際是一個Map資料結構,因此很容易支援其他檢視技術;

7、返回控制權給DispatcherServlet,由DispatcherServlet返回響應給使用者,到此一個流程結束。

此處我們只是講了核心流程,沒有考慮攔截器、本地解析、檔案上傳解析等,後邊再細述。

 

到此,再來看我們前邊提出的問題:

1、  請求如何給前端控制器?這個應該在web.xml中進行部署描述,在HelloWorld中詳細講解。

2、  前端控制器如何根據請求資訊選擇頁面控制器進行功能處理? 我們需要配置HandlerMapping進行對映

3、  如何支援多種頁面控制器呢?配置HandlerAdapter從而支援多種型別的頁面控制器

4、  如何頁面控制器如何使用業務物件?可以預料到,肯定利用Spring IoC容器的依賴注入功能

5、  頁面控制器如何返回模型資料?使用ModelAndView返回

6、  前端控制器如何根據頁面控制器返回的邏輯檢視名選擇具體的檢視進行渲染? 使用ViewResolver進行解析

7、  不同的檢視技術如何使用相應的模型資料? 因為Model是一個Map資料結構,很容易支援其他檢視技術

 

在此我們可以看出具體的核心開發步驟:

1、  DispatcherServlet在web.xml中的部署描述,從而攔截請求到Spring Web MVC

2、  HandlerMapping的配置,從而將請求對映到處理器

3、  HandlerAdapter的配置,從而支援多種型別的處理器

4、  ViewResolver的配置,從而將邏輯檢視名解析為具體檢視技術

5、處理器(頁面控制器)的配置,從而進行功能處理

上邊的開發步驟我們會在Hello World中詳細驗證。

 

2.4、Spring Web MVC優勢

1、清晰的角色劃分:前端控制器(DispatcherServlet)、請求到處理器對映(HandlerMapping)、處理器介面卡(HandlerAdapter)、檢視解析器(ViewResolver)、處理器或頁面控制器(Controller)、驗證器(   Validator)、命令物件(Command  請求引數繫結到的物件就叫命令物件)、表單物件(Form Object 提供給表單展示和提交到的物件就叫表單物件)。

2、分工明確,而且擴充套件點相當靈活,可以很容易擴充套件,雖然幾乎不需要;

3、由於命令物件就是一個POJO,無需繼承框架特定API,可以使用命令物件直接作為業務物件;

4、和Spring 其他框架無縫整合,是其它Web框架所不具備的;

5、可適配,通過HandlerAdapter可以支援任意的類作為處理器;

6、可定製性,HandlerMapping、ViewResolver等能夠非常簡單的定製;

7、功能強大的資料驗證、格式化、繫結機制;

8、利用Spring提供的Mock物件能夠非常簡單的進行Web層單元測試;

9、本地化、主題的解析的支援,使我們更容易進行國際化和主題的切換。

10、強大的JSP標籤庫,使JSP編寫更容易。

………………還有比如RESTful風格的支援、簡單的檔案上傳、約定大於配置的契約式程式設計支援、基於註解的零配置支援等等。

到此我們已經簡單的瞭解了Spring Web MVC,接下來讓我們來個例項來具體使用下這個框架。

 

2.5、Hello World入門

2.5.1、準備開發環境和執行環境:

☆開發工具:eclipse

☆執行環境:tomcat6.0.20

☆工程:動態web工程(springmvc-chapter2)

☆spring框架下載:

spring-framework-3.1.1.RELEASE-with-docs.zip

☆依賴jar包:

1、  Spring框架jar包:

為了簡單,將spring-framework-3.1.1.RELEASE-with-docs.zip/dist/下的所有jar包拷貝到專案的WEB-INF/lib目錄下;

2、  Spring框架依賴的jar包:

需要新增Apache commons logging日誌,此處使用的是commons.logging-1.1.1.jar;

需要新增jstl標籤庫支援,此處使用的是jstl-1.1.2.jar和standard-1.1.2.jar;

 

2.5.2、前端控制器的配置

在我們的web.xml中新增如下配置:

java程式碼:

load-on-startup表示啟動容器時初始化該Servlet;

url-pattern表示哪些請求交給Spring Web MVC處理, “/” 是用來定義預設servlet對映的。也可以如“*.html”表示攔截所有以html為副檔名的請求。

自此請求已交給Spring Web MVC框架處理,因此我們需要配置Spring的配置檔案,預設DispatcherServlet會載入WEB-INF/[DispatcherServlet的Servlet名字]-servlet.xml配置檔案。本示例為WEB-INF/ chapter2-servlet.xml。

 

2.5.3、在Spring配置檔案中配置HandlerMapping、HandlerAdapter

具體配置在WEB-INF/ chapter2-servlet.xml檔案中:

java程式碼:

BeanNameUrlHandlerMapping:表示將請求的URL和Bean名字對映,如URL為 “上下文/hello”,則Spring配置檔案必須有一個名字為“/hello”的Bean,上下文預設忽略。

SimpleControllerHandlerAdapter:表示所有實現了org.springframework.web.servlet.mvc.Controller介面的Bean可以作為Spring Web MVC中的處理器。如果需要其他型別的處理器可以通過實現HadlerAdapter來解決。

 

2.5.4、在Spring配置檔案中配置ViewResolver

具體配置在WEB-INF/ chapter2-servlet.xml檔案中:

java程式碼:

InternalResourceViewResolver:用於支援Servlet、JSP檢視解析;

viewClass:JstlView表示JSP模板頁面需要使用JSTL標籤庫,classpath中必須包含jstl的相關jar包;

prefix和suffix:查詢檢視頁面的字首和字尾(字首[邏輯檢視名]字尾),比如傳進來的邏輯檢視名為hello,則該該jsp檢視頁面應該存放在“WEB-INF/jsp/hello.jsp”;

2.5.5、開發處理器/頁面控制器

java程式碼:

org.springframework.web.servlet.mvc.Controller:頁面控制器/處理器必須實現Controller介面,注意別選錯了;後邊我們會學習其他的處理器實現方式;

public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) :功能處理方法,實現相應的功能處理,比如收集引數、驗證引數、繫結引數到命令物件、將命令物件傳入業務物件進行業務處理、最後返回ModelAndView物件;

ModelAndView:包含了檢視要實現的模型資料和邏輯檢視名;“mv.addObject(“message”, “Hello World!”);

”表示新增模型資料,此處可以是任意POJO物件;“mv.setViewName(“hello”);”表示設定邏輯檢視名為“hello”,檢視解析器會將其解析為具體的檢視,如前邊的檢視解析器InternalResourceVi。wResolver會將其解析為“WEB-INF/jsp/hello.jsp”。

我們需要將其新增到Spring配置檔案(WEB-INF/chapter2-servlet.xml),讓其接受Spring IoC容器管理:

java程式碼:

name=”/hello”:前邊配置的BeanNameUrlHandlerMapping,表示如過請求的URL為 “上下文/hello”,則將會交給該Bean進行處理。

 

2.5.6、開發檢視頁面

建立 /WEB-INF/jsp/hello.jsp檢視頁面:

${message}:表示顯示由HelloWorldController處理器傳過來的模型資料。

2.5.6、啟動伺服器執行測試

通過請求:http://localhost:9080/springmvc-chapter2/hello,如果頁面輸出“Hello World! ”就表明我們成功了!

2.5.7、執行流程分析

如圖2-3

圖2-3

執行步驟:

1、  首先使用者傳送請求http://localhost:9080/springmvc-chapter2/hello——>web容器,web容器根據“/hello”路徑對映到DispatcherServlet(url-pattern為/)進行處理;

2、  DispatcherServlet——>BeanNameUrlHandlerMapping進行請求到處理的對映,BeanNameUrlHandlerMapping將“/hello”路徑直接對映到名字為“/hello”的Bean進行處理,即HelloWorldController,BeanNameUrlHandlerMapping將其包裝為HandlerExecutionChain(只包括HelloWorldController處理器,沒有攔截器);

3、  DispatcherServlet——> SimpleControllerHandlerAdapter,SimpleControllerHandlerAdapter將HandlerExecutionChain中的處理器(HelloWorldController)適配為SimpleControllerHandlerAdapter;

4、  SimpleControllerHandlerAdapter——> HelloWorldController處理器功能處理方法的呼叫,SimpleControllerHandlerAdapter將會呼叫處理器的handleRequest方法進行功能處理,該處理方法返回一個ModelAndView給DispatcherServlet;

5、  hello(ModelAndView的邏輯檢視名)——>InternalResourceViewResolver, InternalResourceViewResolver使用JstlView,具體檢視頁面在/WEB-INF/jsp/hello.jsp;

6、  JstlView(/WEB-INF/jsp/hello.jsp)——>渲染,將在處理器傳入的模型資料(message=HelloWorld!)在檢視中展示出來;

7、  返回控制權給DispatcherServlet,由DispatcherServlet返回響應給使用者,到此一個流程結束。

到此HelloWorld就完成了,步驟是不是有點多?而且回憶下我們主要進行了如下配置:

1、  前端控制器DispatcherServlet;

2、  HandlerMapping

3、  HandlerAdapter

4、  ViewResolver

5、  處理器/頁面控制器

6、  檢視

因此,接下來幾章讓我們詳細看看這些配置,先從DispatcherServlet開始吧。

 

2.6、POST中文亂碼解決方案

spring Web MVC框架提供了org.springframework.web.filter.CharacterEncodingFilter用於解決POST方式造成的中文亂碼問題,具體配置如下:

java程式碼:

以後我們專案及所有頁面的編碼均為UTF-8。

 

2.7、Spring3.1新特性

一、Spring2.5之前,我們都是通過實現Controller介面或其實現來定義我們的處理器類。

 

二、Spring2.5引入註解式處理器支援,通過@Controller 和 @RequestMapping註解定義我們的處理器類。並且提供了一組強大的註解:

 

需要通過處理器對映DefaultAnnotationHandlerMapping和處理器介面卡AnnotationMethodHandlerAdapter來開啟支援@Controller 和 @RequestMapping註解的處理器。

 

@Controller:用於標識是處理器類;

@RequestMapping:請求到處理器功能方法的對映規則;

@RequestParam:請求引數到處理器功能處理方法的方法引數上的繫結;

@ModelAttribute:請求引數到命令物件的繫結;

@SessionAttributes:用於宣告session級別儲存的屬性,放置在處理器類上,通常列出模型屬性(如@ModelAttribute)對應的名稱,則這些屬性會透明的儲存到session中;

@InitBinder:自定義資料繫結註冊支援,用於將請求引數轉換到命令物件屬性的對應型別;

 

三、Spring3.0引入RESTful架構風格支援(通過@PathVariable註解和一些其他特性支援),且又引入了更多的註解支援:

@CookieValue:cookie資料到處理器功能處理方法的方法引數上的繫結;

@RequestHeader:請求頭(header)資料到處理器功能處理方法的方法引數上的繫結;

@RequestBody:請求的body體的繫結(通過HttpMessageConverter進行型別轉換);

@ResponseBody:處理器功能處理方法的返回值作為響應體(通過HttpMessageConverter進行型別轉換);

@ResponseStatus:定義處理器功能處理方法/異常處理器返回的狀態碼和原因;

@ExceptionHandler:註解式宣告異常處理器;

@PathVariable:請求URI中的模板變數部分到處理器功能處理方法的方法引數上的繫結,從而支援RESTful架構風格的URI;

 

四、還有比如:

JSR-303驗證框架的無縫支援(通過@Valid註解定義驗證後設資料);

使用Spring 3開始的ConversionService進行型別轉換(PropertyEditor依然有效),支援使用@NumberFormat 和 @DateTimeFormat來進行數字和日期的格式化;

HttpMessageConverter(Http輸入/輸出轉換器,比如JSON、XML等的資料輸出轉換器);

ContentNegotiatingViewResolver,內容協商檢視解析器,它還是檢視解析器,只是它支援根據請求資訊將同一模型資料以不同的檢視方式展示(如json、xml、html等),RESTful架構風格中很重要的概念(同一資源,多種表現形式);

Spring 3 引入 一個  mvc XML的名稱空間用於支援mvc配置,包括如:

    <mvc:annotation-driven>:

      自動註冊基於註解風格的處理器需要的DefaultAnnotationHandlerMapping、AnnotationMethodHandlerAdapter

      支援Spring3的ConversionService自動註冊

      支援JSR-303驗證框架的自動探測並註冊(只需把JSR-303實現放置到classpath)

      自動註冊相應的HttpMessageConverter(用於支援@RequestBody  和 @ResponseBody)(如XML輸入輸出轉換器(只需將JAXP實現放置到classpath)、JSON輸入輸出轉換器(只需將Jackson實現放置到classpath))等。

    <mvc:interceptors>:註冊自定義的處理器攔截器;

    <mvc:view-controller>:和ParameterizableViewController類似,收到相應請求後直接選擇相應的檢視;

    <mvc:resources>:邏輯靜態資源路徑到物理靜態資源路徑的支援;

    <mvc:default-servlet-handler>:當在web.xml 中DispatcherServlet使用<url-pattern>/</url-pattern> 對映時,能對映靜態資源(當Spring Web MVC框架沒有處理請求對應的控制器時(如一些靜態資源),轉交給預設的Servlet來響應靜態檔案,否則報404找不到資源錯誤,)。

 

……等等。

 

五、Spring3.1新特性:

對Servlet 3.0的全面支援。

 

@EnableWebMvc:用於在基於Java類定義Bean配置中開啟MVC支援,和XML中的<mvc:annotation-driven>功能一樣;

 

新的@Contoller和@RequestMapping註解支援類:處理器對映RequestMappingHandlerMapping 和 處理器介面卡RequestMappingHandlerAdapter組合來代替Spring2.5開始的處理器對映DefaultAnnotationHandlerMapping和處理器介面卡AnnotationMethodHandlerAdapter,提供更多的擴充套件點,它們之間的區別我們在處理器對映一章介紹。

 

新的@ExceptionHandler 註解支援類:ExceptionHandlerExceptionResolver來代替Spring3.0的AnnotationMethodHandlerExceptionResolver,在異常處理器一章我們再詳細講解它們的區別。

 

@RequestMapping的"consumes" 和 "produces" 條件支援:用於支援@RequestBody 和 @ResponseBody,

1consumes指定請求的內容是什麼型別的內容,即本處理方法消費什麼型別的資料,如consumes="application/json"表示JSON型別的內容,Spring會根據相應的HttpMessageConverter進行請求內容區資料到@RequestBody註解的命令物件的轉換;

2produces指定生產什麼型別的內容,如produces="application/json"表示JSON型別的內容,Spring的根據相應的HttpMessageConverter進行請求內容區資料到@RequestBody註解的命令物件的轉換,Spring會根據相應的HttpMessageConverter進行模型資料(返回值)到JSON響應內容的轉換

3以上內容,本章第×××節詳述。

 

URI模板變數增強:URI模板變數可以直接繫結到@ModelAttribute指定的命令物件、@PathVariable方法引數在檢視渲染之前被合併到模型資料中(除JSON序列化、XML混搭場景下)。

 

@Validated:JSR-303的javax.validation.Valid一種變體(非JSR-303規範定義的,而是Spring自定義的),用於提供對Spring的驗證器(org.springframework.validation.Validator)支援,需要Hibernate Validator 4.2及更高版本支援;

 

@RequestPart:提供對“multipart/form-data”請求的全面支援,支援Servlet 3.0檔案上傳(javax.servlet.http.Part)、支援內容的HttpMessageConverter(即根據請求頭的Content-Type,來判斷內容區資料是什麼型別,如JSON、XML,能自動轉換為命令物件),比@RequestParam更強大(只能對請求引數資料繫結,key-alue格式),而@RequestPart支援如JSON、XML內容區資料的繫結;詳見本章的第×××節;

 

Flash 屬性 和 RedirectAttribute:通過FlashMap儲存一個請求的輸出,當進入另一個請求時作為該請求的輸入,典型場景如重定向(POST-REDIRECT-GET模式,1、POST時將下一次需要的資料放在FlashMap;2、重定向;3、通過GET訪問重定向的地址,此時FlashMap會把1放到FlashMap的資料取出放到請求中,並從FlashMap中刪除;從而支援在兩次請求之間儲存資料並防止了重複表單提交)。

Spring Web MVC提供FlashMapManager用於管理FlashMap,預設使用SessionFlashMapManager,即資料預設儲存在session中

 

相關文章