三個Servlet
一、HttpServletBean
參與創(chuàng)建工作,沒有涉及請求的處理
二傻盟、FrameworkServlet
當?shù)谝粋€請求過來時,就初始化容器
init.png
如上圖嫂丙,當?shù)谝粋€請求過來時娘赴,F(xiàn)rameworkServlet就初始化容器,并將Spring的容器進行刷新跟啤,而DispatcherServlet的onRefresh刷新只是初始化九大組件诽表。以后的請求就都不需要初始化容器媳叨。
初始化完容器以后,因為FrameworkServlet重寫了Servlet的service关顷,doGet糊秆,doPost等方法,所以會先走到Framework的doGet或者doPost等請求方法议双。
FrameworkServlet的設(shè)計很有趣痘番,它將所有的請求get,post,delete等請求全部嫁接到processRequest(request,response)
processRequest.png
在processRequest(request,response)方法中又調(diào)用了doService(request,response)方法,該方法是在DispatcherServlet中具體實現(xiàn)
所以說FrameworkServlet主要有兩個作用:
1.創(chuàng)建并初始化Spring容器
2.請求的入口
三平痰、DispatcherServlet
第一個作用是初始化九大組件汞舱,就是在上面的FrameworkServlet執(zhí)行完初始化容器后,就調(diào)用onRefresh宗雇,方法里面只調(diào)用了initStrategies(context)方法昂芜,initStrategies方法才是重點,如下圖
nine_components.png
每個組件在處理請求時都有可能會用到
第二個作用就是處理請求
請求的入口是doService 赔蒲,我們看下請求過來的調(diào)用鏈
image.png
doService對request設(shè)置了一些屬性
doService.png
接著調(diào)用doDispatch泌神,doDispatch是DispatcherServlet的核心
我們看代碼
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
/**
*
* doDispatch中最重要的代碼總共有四句(見下面的標簽數(shù)字)
*
* Handler、HandlerMapping舞虱、HandlerAdapter三者的關(guān)系:
* Handler:就是我們的控制器Controller中加了@XXXMapping的方法
* HandlerMapping: 用來快速查找Handler
* HandlerAdapter:調(diào)用Handler來干活欢际,而且不同Handler需要不同的Adapter
* 這就好比HandlerAdapter是工人,Handler是工具矾兜,HandlerMapping是根據(jù)加工的需求來選擇用
* 什么設(shè)備
*/
/**
* 封裝Request损趋,如果不是上傳請求則直接使用接收到的request
* 如果是上傳請求,重新封裝成MultipartHttpServletRequest
*/
HttpServletRequest processedRequest = request;
/**
* 處理請求的處理器鏈
* 包含有處理器Handler和對應(yīng)的攔截器Interceptor
*/
HandlerExecutionChain mappedHandler = null;
/**
* 是否為上傳請求的標記
*/
boolean multipartRequestParsed = false;
/**
* 從request中獲取異步請求
*/
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
/**
* View 跟 ViewResolver
* View 是用來展示數(shù)據(jù)的
* 而ViewResolver是用來查找View的
* 做完請求工作后椅寺,需要返回結(jié)果浑槽,而返回結(jié)果就需要模板,
* View就是所需要的模板返帕,ViewResolver就是來選擇哪個模板
*
* **/
ModelAndView mv = null;
/**
* 異常聲明
* doDispatch()中對異常又兩種處理方法:
* 一桐玻、如果是處理請求中出現(xiàn)的異常,會捕獲并在processDispatchResult中渲染到最后的視圖中
* 二溉旋、如果是渲染中出現(xiàn)異常畸冲,則直接拋出
*/
Exception dispatchException = null;
try {
// 檢查是不是上傳請求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
/** 第一句
* 使用HandlerMapping找到可以干活的Handler
*
* **/
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 找不到Handler返回404
noHandlerFound(processedRequest, response);
return;
}
/** 第二句
* 找到合適的HandlerAdapter去讓他干活
* **/
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 處理GET、HEAD請求的last-modified
// Process last-modified header, if supported by the handler.
/**
* Last-Modified是HTTP頭部的一種屬性观腊,表示當前請求的資源跟上一次請求的資源是否相同
* 如果相同邑闲,返回304并且沒有新的實體(body)返回
* 否則返回新的實體內(nèi)容
*
* 在瀏覽器第一次請求某一個URL時,服務(wù)器端的返回狀態(tài)會是200梧油,內(nèi)容是客戶端請求的資源苫耸,
* 同時有一個Last-Modified的屬性標記此文件在服務(wù)器端最后被修改的時間。
* 客戶端第二次請求此URL時,根據(jù)HTTP協(xié)議的規(guī)定烙博,瀏覽器會向服務(wù)器傳送If-Modified-Since報頭,詢問該時間之后文件是否有被修改過
* 如果服務(wù)器端的資源沒有變化飞涂,則自動返回 HTTP 304(Not Changed.)狀態(tài)碼嫌褪,內(nèi)容為空呀枢,這樣就節(jié)省了傳輸數(shù)據(jù)量。
* 當服務(wù)器端代碼發(fā)生改變或者重啟服務(wù)器時笼痛,則重新發(fā)出資源裙秋,
* 返回和第一次請求時類似。從而保證不向客戶端重復發(fā)出資源缨伊,也保證當服務(wù)器有變化時摘刑,客戶端能夠得到最新的資源。
*/
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
/** 如果有攔截器刻坊,就餓執(zhí)行我們的攔截器枷恕,preHandle前置處理**/
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
/** 第三句
* 讓HandlerAdapter開始干活,干完活后返回數(shù)據(jù)
* **/
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 如果需要異步處理谭胚,則直接返回
/**
* 因為異步處理會重新開啟一個線程去執(zhí)行結(jié)果的返回
* 不會占用目前這個線程徐块,所以可以直接返回
*/
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 當view為空時(比如,handler返回類型為void)漏益,根據(jù)request設(shè)置默認view
applyDefaultViewName(processedRequest, mv);
/** 執(zhí)行了攔截器的后置處理 postHandle**/
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
/** 第四句
* 將數(shù)據(jù)處理蛹锰,通過View展示給用戶
* 處理結(jié)果深胳,包括處理異常绰疤,渲染頁面,發(fā)出完成通知舞终,觸發(fā)攔截器的afterCompletion
* **/
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
// 判斷是否為異步請求
if (asyncManager.isConcurrentHandlingStarted()) {
// 執(zhí)行異步的攔截器
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 刪除上傳請求的資源轻庆,不然會產(chǎn)生臨時的資源
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
doDispatch方法主要有四個任務(wù):1.用request找到Handler; 2.根據(jù)Handler找到HandlerAdapter敛劝; 3.用HandlerAdapter處理Handler 4.調(diào)用processDispatchResult方法處理結(jié)果(包括異常的處理和View視圖的渲染)余爆。
除此之外還另外做了許多事情,比如判斷是否為上傳文件請求夸盟,是否帶有Last-Modified頭部蛾方,執(zhí)行攔截器,是否為異步請求...
processDispatchResult方法處理上面doDispatch返回的結(jié)果上陕,包括處理異常桩砰、渲染頁面、觸發(fā)攔截器的后置處理释簿,處理異常只是處理doDispatch()方法中的異常亚隅、渲染頁面的異常就拋出。渲染頁面的方法調(diào)用render