您好,登錄后才能下訂單哦!
Spring Web MVC是一種基于Java的實現(xiàn)了Web MVC設(shè)計模式的請求驅(qū)動類型的輕量級Web框架,即使用了MVC架構(gòu)模式的思想,將web層進(jìn)行職責(zé)解耦,基于請求驅(qū)動指的就是使用請求-響應(yīng)模型,框架的目的就是幫助我們簡化開發(fā),Spring Web MVC也是要簡化我們?nèi)粘eb開發(fā)的。
另外還有一種基于組件的、事件驅(qū)動的Web框架在此就不介紹了,如Tapestry、JSF等。
Spring Web MVC也是服務(wù)到工作者模式的實現(xiàn),但進(jìn)行可優(yōu)化。前端控制器是DispatcherServlet;
應(yīng)用控制器其實拆為處理器映射器(Handler Mapping)進(jìn)行處理器管理和視圖解析器(View Resolver)進(jìn)行視圖管理;頁面控制器/動作/處理器為Controller接口(僅包含ModelAndView handleRequest(request, response)
方法)的實現(xiàn)(也可以是任何的POJO類);支持本地化(Locale)解析、主題(Theme)解析及文件上傳等;提供了非常靈活的數(shù)據(jù)驗證、格式化和數(shù)據(jù)綁定機制;提供了強大的約定大于配置(慣例優(yōu)先原則)的契約式編程支持。
√讓我們能非常簡單的設(shè)計出干凈的Web層和薄薄的Web層;
√進(jìn)行更簡潔的Web層的開發(fā);
√天生與Spring框架集成(如IoC容器、AOP等);
√提供強大的約定大于配置的契約式編程支持;
√能簡單的進(jìn)行Web層的單元測試;
√支持靈活的URL到頁面控制器的映射;
√非常容易與其他視圖技術(shù)集成,如Velocity、FreeMarker等等,因為模型數(shù)據(jù)不放在特定的API里,而是放在一個Model里(Map
數(shù)據(jù)結(jié)構(gòu)實現(xiàn),因此很容易被其他框架使用);
√非常靈活的數(shù)據(jù)驗證、格式化和數(shù)據(jù)綁定機制,能使用任何對象進(jìn)行數(shù)據(jù)綁定,不必實現(xiàn)特定框架的API;
√提供一套強大的JSP標(biāo)簽庫,簡化JSP開發(fā);
√支持靈活的本地化、主題等解析;
√更加簡單的異常處理;
√對靜態(tài)資源的支持;
√支持Restful風(fēng)格。
Spring Web MVC框架也是一個基于請求驅(qū)動的Web框架,并且也使用了前端控制器模式來進(jìn)行設(shè)計,再根據(jù)請求映射規(guī)則分發(fā)給相應(yīng)的頁面控制器(動作/處理器)進(jìn)行處理。首先讓我們整體看一下Spring Web MVC處理請求的流程:
如圖2-1
圖2-1
具體執(zhí)行步驟如下:
1、 首先用戶發(fā)送請求————>前端控制器,前端控制器根據(jù)請求信息(如URL)來決定選擇哪一個頁面控制器進(jìn)行處理并把請求委托給它,即以前的控制器的控制邏輯部分;圖2-1中的1、2步驟;
2、 頁面控制器接收到請求后,進(jìn)行功能處理,首先需要收集和綁定請求參數(shù)到一個對象,這個對象在Spring Web MVC中叫命令對象,并進(jìn)行驗證,然后將命令對象委托給業(yè)務(wù)對象進(jìn)行處理;處理完畢后返回一個ModelAndView(模型數(shù)據(jù)和邏輯視圖名);圖2-1中的3、4、5步驟;
3、 前端控制器收回控制權(quán),然后根據(jù)返回的邏輯視圖名,選擇相應(yīng)的視圖進(jìn)行渲染,并把模型數(shù)據(jù)傳入以便視圖渲染;圖2-1中的步驟6、7;
4、 前端控制器再次收回控制權(quán),將響應(yīng)返回給用戶,圖2-1中的步驟8;至此整個結(jié)束。
問題:
1、 請求如何給前端控制器?
2、 前端控制器如何根據(jù)請求信息選擇頁面控制器進(jìn)行功能處理?
3、 如何支持多種頁面控制器呢?
4、 如何頁面控制器如何使用業(yè)務(wù)對象?
5、 頁面控制器如何返回模型數(shù)據(jù)?
6、 前端控制器如何根據(jù)頁面控制器返回的邏輯視圖名選擇具體的視圖進(jìn)行渲染?
7、 不同的視圖技術(shù)如何使用相應(yīng)的模型數(shù)據(jù)?
首先我們知道有如上問題,那這些問題如何解決呢?請讓我們先繼續(xù),在后邊依次回答。
1、Spring Web MVC核心架構(gòu)圖,如圖2-2
圖2-2
架構(gòu)圖對應(yīng)的DispatcherServlet核心代碼如下:
java代碼:
Java代碼
//前端控制器分派方法
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
try {
ModelAndView mv;
boolean errorView = false;
try {
//檢查是否是請求是否是multipart(如文件上傳),如果是將通過MultipartResolver解析
processedRequest = checkMultipart(request);
//步驟2、請求到處理器(頁面控制器)的映射,通過HandlerMapping進(jìn)行映射
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//步驟3、處理器適配,即將我們的處理器包裝成相應(yīng)的適配器(從而支持多種類型的處理器)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 304 Not Modified緩存支持
//此處省略具體代碼
// 執(zhí)行處理器相關(guān)的攔截器的預(yù)處理(HandlerInterceptor.preHandle)
//此處省略具體代碼
// 步驟4、由適配器執(zhí)行處理器(調(diào)用處理器相應(yīng)功能處理方法)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// 執(zhí)行處理器相關(guān)的攔截器的后處理(HandlerInterceptor.postHandle)
//此處省略具體代碼
}
catch (ModelAndViewDefiningException ex) {
logger.debug("ModelAndViewDefiningException encountered", ex);
mv = ex.getModelAndView();
}
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
//步驟5 步驟6、解析視圖并進(jìn)行視圖的渲染
//步驟5 由ViewResolver解析View(viewResolver.resolveViewName(viewName, locale))
//步驟6 視圖在渲染時會把Model傳入(view.render(mv.getModelInternal(), request, response);)
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
// 執(zhí)行處理器相關(guān)的攔截器的完成后處理(HandlerInterceptor.afterCompletion)
//此處省略具體代碼
catch (Exception ex) {
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
catch (Error err) {
ServletException ex = new NestedServletException("Handler processing failed", err);
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
finally {
// Clean up any resources used by a multipart request.
if (processedRequest != request) {
cleanupMultipart(processedRequest);
}
}
}
核心架構(gòu)的具體流程步驟如下:
1、 首先用戶發(fā)送請求——>DispatcherServlet,前端控制器收到請求后自己不進(jìn)行處理,而是委托給其他的解析器進(jìn)行處理,作為統(tǒng)一訪問點,進(jìn)行全局的流程控制;
2、 DispatcherServlet——>HandlerMapping, HandlerMapping將會把請求映射為HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象、多個HandlerInterceptor攔截器)對象,通過這種策略模式,很容易添加新的映射策略;
3、 DispatcherServlet——>HandlerAdapter,HandlerAdapter將會把處理器包裝為適配器,從而支持多種類型的處理器,即適配器設(shè)計模式的應(yīng)用,從而很容易支持很多類型的處理器;
4、 HandlerAdapter——>處理器功能處理方法的調(diào)用,HandlerAdapter將會根據(jù)適配的結(jié)果調(diào)用真正的處理器的功能處理方法,完成功能處理;并返回一個ModelAndView對象(包含模型數(shù)據(jù)、邏輯視圖名);
5、 ModelAndView的邏輯視圖名——> ViewResolver, ViewResolver將把邏輯視圖名解析為具體的View,通過這種策略模式,很容易更換其他視圖技術(shù);
6、 View——>渲染,View會根據(jù)傳進(jìn)來的Model模型數(shù)據(jù)進(jìn)行渲染,此處的Model實際是一個Map數(shù)據(jù)結(jié)構(gòu),因此很容易支持其他視圖技術(shù);
7、返回控制權(quán)給DispatcherServlet,由DispatcherServlet返回響應(yīng)給用戶,到此一個流程結(jié)束。
此處我們只是講了核心流程,沒有考慮攔截器、本地解析、文件上傳解析等,后邊再細(xì)述。
到此,再來看我們前邊提出的問題:
1、 請求如何給前端控制器?這個應(yīng)該在web.xml中進(jìn)行部署描述,在HelloWorld中詳細(xì)講解。
2、 前端控制器如何根據(jù)請求信息選擇頁面控制器進(jìn)行功能處理? 我們需要配置HandlerMapping進(jìn)行映射
3、 如何支持多種頁面控制器呢?配置HandlerAdapter從而支持多種類型的頁面控制器
4、 如何頁面控制器如何使用業(yè)務(wù)對象?可以預(yù)料到,肯定利用Spring IoC容器的依賴注入功能
5、 頁面控制器如何返回模型數(shù)據(jù)?使用ModelAndView返回
6、 前端控制器如何根據(jù)頁面控制器返回的邏輯視圖名選擇具體的視圖進(jìn)行渲染? 使用ViewResolver進(jìn)行解析
7、 不同的視圖技術(shù)如何使用相應(yīng)的模型數(shù)據(jù)? 因為Model是一個Map數(shù)據(jù)結(jié)構(gòu),很容易支持其他視圖技術(shù)
在此我們可以看出具體的核心開發(fā)步驟:
1、 DispatcherServlet在web.xml中的部署描述,從而攔截請求到Spring Web MVC
2、 HandlerMapping的配置,從而將請求映射到處理器
3、 HandlerAdapter的配置,從而支持多種類型的處理器
4、 ViewResolver的配置,從而將邏輯視圖名解析為具體視圖技術(shù)
5、處理器(頁面控制器)的配置,從而進(jìn)行功能處理
上邊的開發(fā)步驟我們會在Hello World中詳細(xì)驗證。
1、清晰的角色劃分:前端控制器(DispatcherServlet
)、請求到處理器映射(HandlerMapping)、處理器適配器(HandlerAdapter)、視圖解析器(ViewResolver)、處理器或頁面控制器(Controller)、驗證器( Validator)、命令對象(Command 請求參數(shù)綁定到的對象就叫命令對象)、表單對象(Form Object 提供給表單展示和提交到的對象就叫表單對象)。
2、分工明確,而且擴展點相當(dāng)靈活,可以很容易擴展,雖然幾乎不需要;
3、由于命令對象就是一個POJO,無需繼承框架特定API,可以使用命令對象直接作為業(yè)務(wù)對象;
4、和Spring 其他框架無縫集成,是其它Web框架所不具備的;
5、可適配,通過HandlerAdapter可以支持任意的類作為處理器;
6、可定制性,HandlerMapping、ViewResolver等能夠非常簡單的定制;
7、功能強大的數(shù)據(jù)驗證、格式化、綁定機制;
8、利用Spring提供的Mock對象能夠非常簡單的進(jìn)行Web層單元測試;
9、本地化、主題的解析的支持,使我們更容易進(jìn)行國際化和主題的切換。
10、強大的JSP標(biāo)簽庫,使JSP編寫更容易。
………………還有比如RESTful風(fēng)格的支持、簡單的文件上傳、約定大于配置的契約式編程支持、基于注解的零配置支持等等。
到此我們已經(jīng)簡單的了解了Spring Web MVC,接下來讓我們來個實例來具體使用下這個框架。
☆開發(fā)工具:eclipse
☆運行環(huán)境:tomcat6.0.20
☆工程:動態(tài)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標(biāo)簽庫支持,此處使用的是jstl-1.1.2.jar和standard-1.1.2.jar;
在我們的web.xml中添加如下配置:
java代碼:
Java代碼
<servlet>
<servlet-name>chapter2</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>chapter2</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
load-on-startup:表示啟動容器時初始化該Servlet;
url-pattern:表示哪些請求交給Spring Web MVC處理, “/” 是用來定義默認(rèn)servlet映射的。也可以如“*.html”表示攔截所有以html為擴展名的請求。
自此請求已交給Spring Web MVC框架處理,因此我們需要配置Spring的配置文件,默認(rèn)DispatcherServlet會加載WEB-INF/[DispatcherServlet的Servlet名字]-servlet.xml配置文件。本示例為WEB-INF/ chapter2-servlet.xml。
具體配置在WEB-INF/ chapter2-servlet.xml文件中:
java代碼:
Java代碼
<!-- HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- HandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
BeanNameUrlHandlerMapping:表示將請求的URL和Bean名字映射,如URL為 “上下文/hello”,則Spring配置文件必須有一個名字為“/hello”的Bean,上下文默認(rèn)忽略。
SimpleControllerHandlerAdapter:表示所有實現(xiàn)了org.springframework.web.servlet.mvc.Controller接口的Bean可以作為Spring Web MVC中的處理器。如果需要其他類型的處理器可以通過實現(xiàn)HadlerAdapter來解決。
具體配置在WEB-INF/ chapter2-servlet.xml文件中:
java代碼:
Java代碼
<!-- ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
InternalResourceViewResolver:用于支持Servlet、JSP視圖解析;
viewClass:JstlView表示JSP模板頁面需要使用JSTL標(biāo)簽庫,classpath中必須包含jstl的相關(guān)jar包;
prefix和suffix:查找視圖頁面的前綴和后綴(前綴[邏輯視圖名]后綴),比如傳進(jìn)來的邏輯視圖名為hello,則該該jsp視圖頁面應(yīng)該存放在“WEB-INF/jsp/hello.jsp”;
java代碼:
Java代碼
package cn.javass.chapter2.web.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。