一、作用
DispatcherServlet是前端控制器設(shè)計(jì)模式的實(shí)現(xiàn),提供spring Web MVC的集中訪問(wèn)點(diǎn),而且負(fù)責(zé)職責(zé)的分派,而且與Spring IoC容器無(wú)縫集成,從而可以獲得Spring的所有好處 (設(shè)計(jì)模式一套被反復(fù)使用,代碼設(shè)計(jì)方面的經(jīng)驗(yàn)總結(jié).使用設(shè)計(jì)模式的主要目的是為了能攻更好的獲得可重用行,這包括體系結(jié)構(gòu)和代碼實(shí)現(xiàn)等方面,并能夠讓開發(fā)者開發(fā)出來(lái)的軟件更容易被他人理解,同事保證系統(tǒng)結(jié)構(gòu)的正確性和代碼的可靠性)
二、 什么前端控制器設(shè)計(jì)模式
前端控制器模式(Front Controller Pattern)通俗來(lái)講是用來(lái)提供一個(gè)集中的請(qǐng)求處理機(jī)制,所有的請(qǐng)求都將由一個(gè)單一的處理程序處理。該處理程序比如可以做認(rèn)證/授權(quán)/記錄日志,或者跟蹤請(qǐng)求,然后把請(qǐng)求傳給相應(yīng)的處理程序。
- 你想要避免重復(fù)控制邏輯
- 你想要對(duì)多個(gè)請(qǐng)求采取共同的處理邏輯。
- 你想把系統(tǒng)處理代碼和視圖分隔開(不要在視圖中寫控制代碼)
- 你想要把系統(tǒng)的訪問(wèn)點(diǎn)集中在一點(diǎn)
前端控制器為處理請(qǐng)求提供了一個(gè)集中的入口點(diǎn)。因此能夠集中控制邏輯,因此就減少了直接置入視圖的代碼量。
1、前端控制器模式
- 前端控制器(Front Controller) - 處理應(yīng)用程序所有類型請(qǐng)求的單個(gè)處理程序,應(yīng)用程序可以是基于 web 的應(yīng)用程序,也可以是基于桌面的應(yīng)用程序。
- 調(diào)度器(Dispatcher)- 前端控制器可能使用一個(gè)調(diào)度器對(duì)象來(lái)調(diào)度請(qǐng)求到相應(yīng)的具體處理程序。
- 視圖(View)視圖是為請(qǐng)求而創(chuàng)建的對(duì)象。
2、 操作步驟
- 前端控制器(Front Controller) - 處理應(yīng)用程序所有類型請(qǐng)求的單個(gè)處理程序,應(yīng)用程序可以是基于 web 的應(yīng)用程序,也可以是基于桌面的應(yīng)用程序。
- 調(diào)度器(Dispatcher) - 前端控制器可能使用一個(gè)調(diào)度器對(duì)象來(lái)調(diào)度請(qǐng)求到相應(yīng)的具體處理程序。
- 視圖(View) - 視圖是為請(qǐng)求而創(chuàng)建的對(duì)象。
3、栗子
- 創(chuàng)建視圖對(duì)象
public class IndexView { public void show() { System.out.println("歡迎進(jìn)入首頁(yè)"); } } public class IndexView { public void show() { System.out.println("游客界面"); } } - 創(chuàng)建Dispatcher
public class Dispatcher { private IndexView indexView; private GuestView guestView; public Dispatcher(){ indexView = new IndexView(); guestView = new GuestView(); } public void dispatch(String request){ if(request.equalsIgnoreCase("login")){ indexView.show(); }else{ guestView.show(); } } } - 創(chuàng)建FrontController
public class FrontController { private Dispatcher dispatcher; public FrontController(){ dispatcher = new Dispatcher(); } private boolean isLoginUser(){ System.out.println(); return true; } private void trackRequest(String request){ System.out.println("頁(yè)面: " + request); } public void dispatchRequest(String request){ //記錄請(qǐng)求 trackRequest(request); if(isLoginUser()){ dispatcher.dispatch(request); } } } - 測(cè)試代碼
public static void main(String[] args) { FrontController controller = new FrontController(); controller.dispatchRequest("1111"); }
4、結(jié)構(gòu)圖

三、繼承結(jié)構(gòu)圖

四、DispatcherServlet原理
1、說(shuō)明
前端控制器是整個(gè)MVC框架中最為核心的一塊,它主要用來(lái)攔截符合要求的外部請(qǐng)求,并把請(qǐng)求分發(fā)到不同的控制器去處理,根據(jù)控制器處理后的結(jié)果,生成相應(yīng)的響應(yīng)發(fā)送到客戶端。前端控制器既可以使用Filter實(shí)現(xiàn)(Struts2采用這種方式),也可以使用Servlet來(lái)實(shí)現(xiàn)(spring MVC框架)
2、MVC結(jié)構(gòu)
- M-Model 模型(完成業(yè)務(wù)邏輯:有javaBean構(gòu)成,service+dao+entity)
- V-View 視圖(做界面的展示 jsp,html)
- C-Controller 控制器(接收請(qǐng)求—>調(diào)用模型—>根據(jù)結(jié)果派發(fā)頁(yè)面)
3、DIspatcher執(zhí)行流程圖
執(zhí)行原理圖

步驟說(shuō)明
1、 首先用戶發(fā)送請(qǐng)求——>DispatcherServlet,前端控制器收到請(qǐng)求后自己不進(jìn)行處理,而是委托給其他的解析器進(jìn)行處理,作為統(tǒng)一訪問(wèn)點(diǎn),進(jìn)行全局的流程控制;
2、 DispatcherServlet——>HandlerMapping, HandlerMapping將會(huì)把請(qǐng)求映射為HandlerExecutionChain對(duì)象(包含一個(gè)Handler處理器(頁(yè)面控制器)對(duì)象、多個(gè)HandlerInterceptor攔截器)對(duì)象,通過(guò)這種策略模式,很容易添加新的映射策略;
3、 DispatcherServlet——>HandlerAdapter,HandlerAdapter將會(huì)把處理器包裝為適配器,從而支持多種類型的處理器,即適配器設(shè)計(jì)模式的應(yīng)用,從而很容易支持很多類型的處理器;
4、 HandlerAdapter——>處理器功能處理方法的調(diào)用,HandlerAdapter將會(huì)根據(jù)適配的結(jié)果調(diào)用真正的處理器的功能處理方法,完成功能處理;并返回一個(gè)ModelAndView對(duì)象(包含模型數(shù)據(jù)、邏輯視圖名);
5、 ModelAndView的邏輯視圖名——> ViewResolver, ViewResolver將把邏輯視圖名解析為具體的View,通過(guò)這種策略模式,很容易更換其他視圖技術(shù);
6、 View——>渲染,View會(huì)根據(jù)傳進(jìn)來(lái)的Model模型數(shù)據(jù)進(jìn)行渲染,此處的Model實(shí)際是一個(gè)Map數(shù)據(jù)結(jié)構(gòu),因此很容易支持其他視圖技術(shù);
7、返回控制權(quán)給DispatcherServlet,由DispatcherServlet返回響應(yīng)給用戶
核心代碼
//前端控制器分派方法
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 {
//檢查是否是請(qǐng)求是否是multipart(如文件上傳),如果是將通過(guò)MultipartResolver解析
processedRequest = checkMultipart(request);
//步驟2、請(qǐng)求到處理器(頁(yè)面控制器)的映射,通過(guò)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 視圖在渲染時(shí)會(huì)把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);
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
finally {
if (processedRequest != request) {
cleanupMultipart(processedRequest);
}
}
}
五、DispatcherServlet輔助類
1、說(shuō)明
SpringMVC中的DispatcherServlet使用一些特殊的bean來(lái)處理request請(qǐng)求和渲染合適的視圖。這些bean就是Spring MVC中的一部分。你能夠通過(guò)在WebApplicationContext中的一個(gè)或多個(gè)配置來(lái)使用這些特殊的bean。但是,你不需要在Spring MVC在維護(hù)這些默認(rèn)要使用的bean時(shí),去把那些沒(méi)有配置過(guò)的bean都去初始化一道。在下一部分中,首先讓我們看看在DispatcherServlet依賴的那些特殊bean類型
2、屬性
| bean類型 | 說(shuō)明 |
|---|---|
| Controller | 處理器/頁(yè)面控制器,做的是MVC中的C的事情,但控制邏輯轉(zhuǎn)移到前端控制器了,用于對(duì)請(qǐng)求進(jìn)行處理 |
| HandlerMapping | 請(qǐng)求到處理器的映射,如果映射成功返回一個(gè)HandlerExecutionChain對(duì)象(包含一個(gè)Handler處理器(頁(yè)面控制器)對(duì)象、多個(gè)HandlerInterceptor攔截器)對(duì)象;如BeanNameUrlHandlerMapping將URL與Bean名字映射,映射成功的Bean就是此處的處理器 |
| HandlerAdapter | HandlerAdapter將會(huì)把處理器包裝為適配器,從而支持多種類型的處理器,即適配器設(shè)計(jì)模式的應(yīng)用,從而很容易支持很多類型的處理器;如SimpleControllerHandlerAdapter將對(duì)實(shí)現(xiàn)了Controller接口的Bean進(jìn)行適配,并且掉處理器的handleRequest方法進(jìn)行功能處理 |
| HandlerExceptionResolver處理器異常解析器 | 處理器異常解析,可以將異常映射到相應(yīng)的統(tǒng)一錯(cuò)誤界面,從而顯示用戶友好的界面(而不是給用戶看到具體的錯(cuò)誤信息) |
| ViewResolver視圖解析器 | ViewResolver將把邏輯視圖名解析為具體的View,通過(guò)這種策略模式,很容易更換其他視圖技術(shù);如InternalResourceViewResolver將邏輯視圖名映射為jsp視圖 |
| LocaleResolver & LocaleContextResolver地區(qū)解析器和地區(qū)Context解析器 | 解析客戶端中使用的地區(qū)和時(shí)區(qū),用來(lái)提供不同的國(guó)際化的view視圖。 |
| ThemeResolver | 主題解析器,解析web應(yīng)用中能夠使用的主題,比如提供個(gè)性化的網(wǎng)頁(yè)布局。 |
| MultipartResolver | 多部件解析器,主要處理multi-part(多部件)request請(qǐng)求,例如:在HTML表格中處理文件上傳。 |
| FlashMapManager | FlashMap管理器儲(chǔ)存并檢索在"input"和"output"的FlashMap中可以在request請(qǐng)求間(通常是通過(guò)重定向)傳遞屬性的FlashMap, |