目錄
一,DispatcherServlet繼承結構
二、SpringMvc請求處理的大致流程
2.1 Handler方法執行的時機
打斷點:
觀察呼叫棧:
doDispathch⽅法中的1064⾏程式碼完成handler⽅法的調⽤
2.2 頁面渲染時機(打斷點並觀察呼叫棧)
3.3 doDispatch()方法核心步驟 (Springmvc處理請求的大致流程):
- 呼叫
getHandler()
獲取到能夠處理當前請求的執行鏈HandlerExecutionChain
(Handler + 攔截器) - 呼叫
getHandlerAdapter()
獲取能夠執行Handler的介面卡 - 介面卡呼叫Handler執行
ha.handle()
,總會返回一個ModelAndView物件 - 呼叫
processDispatchResult()
方法完成檢視跳轉
//org.springframework.web.servlet.DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
//執行器鏈,包含了handler和一些攔截器
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//非同步管理器
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//1. 檢查是否是檔案上傳的請求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
/*
2. 取得處理當前請求的Controller,這裡也稱為Handler,即處理器。這裡並不是直接返回controller,
而是返回HandlerExecutionChain 請求處理鏈物件 該物件封裝了Handler和Inteceptor
*/
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果handler為空, 則返回404
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 3. 獲取處理請求的處理器介面卡 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 處理last-modeified 請求頭
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;
}
}
//===============攔截器的第一個攔截時機
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 4 實際處理器處理請求,返回結果檢視物件
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//結果檢視物件的處理
applyDefaultViewName(processedRequest, mv);
//==============攔截器的第二個攔截時機
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);
}
//跳轉檢視頁面,渲染檢視
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//最終會呼叫HandlerInterceptor的afterCompletion方法
//========攔截器的第三個攔截時機————檢視頁面渲染完成之後攔截
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//最終會呼叫HandlerInterceptor的afterCompletion方法
//========攔截器的第三個攔截時機————檢視頁面渲染完成之後攔截
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
三,getHandler()方法分析
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//handlerMappings是一個 ArrayList ———— List<HandlerMapping> handlerMappings
//而HandlerMapping 則儲存了url和handler的對映關係
if (this.handlerMappings != null) {
/**
* 遍歷 handlerMappings :
* 1. BeanNameUrlHandlerMapping ,早期的一種使用方式
* 2. RequestMappingHandlerMapping,我們的請求所使用的
*/
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
HandlerExecutionChain 包含了 DemoController.handle01 以及 0個 interceptors
Q:HandlerMapping裡面的對映關係是在何時進行初始化的?
A:在容器啟動時時,IOC容器在掃描@Controller物件時會掃描@RequestMapping註解,然後就可以建立url和handler方法的對映關係
四,getHandlerAdapter()方法——介面卡獲取分析
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//List<HandlerAdapter> handlerAdapters
if (this.handlerAdapters != null) {
/*
遍歷 handlerAdapters:
遍歷各個HandlerAdapter,看哪個Adapter⽀持處理當前Handler:
handlerAdapters是一個List<HandlerAdapter>,HandlerAdapter是一個介面,裡面有幾個實現類:
1. HttpRequestHandlerAdapter ——實現介面的方式
2. SimpleControllerHandlerAdapter ——實現Controller介面的方式
3. RequestMappingHandlerAdapter ——是不是HandlerMethod(RequestMappingHandlerMapping封裝的)的例項
*/
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
Q:handlerAdapters 中三個 實現類是如何初始化的?
A:這些元件簡單的說也是容器啟動時初始化的。
五,SpringMVC九大元件初始化
5.1 九大元件
//org.springframework.web.servlet.DispatcherServlet
//多部件解析器,檔案上傳之類的
@Nullable
private MultipartResolver multipartResolver;
//區域化 國際化解析器
@Nullable
private LocaleResolver localeResolver;
//主題解析器
@Nullable
private ThemeResolver themeResolver;
//處理器對映器元件
@Nullable
private List<HandlerMapping> handlerMappings;
//處理器介面卡元件
@Nullable
private List<HandlerAdapter> handlerAdapters;
//異常解析器
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
//預設檢視名轉換器元件
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
//flash屬性管理元件
@Nullable
private FlashMapManager flashMapManager;
//檢視解析器
@Nullable
private List<ViewResolver> viewResolvers;
上述九大元件都是定義了介面,介面其實是定義了規範。
5.2 九大元件初始化細節:
//org.springframework.web.servlet.DispatcherServlet
//主要完成元件的初始化
@Override
protected void onRefresh(ApplicationContext context) {
//初始化策略
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
// 多檔案上傳的元件
initMultipartResolver(context);
// 初始化本地語語言環境
initLocaleResolver(context);
// 初始化模板處理器
initThemeResolver(context);
// 初始化HandlerMapping
initHandlerMappings(context);
// 初始化引數介面卡
initHandlerAdapters(context);
// 初始化異常攔截器
initHandlerExceptionResolvers(context);
// 初始化檢視前處理器
initRequestToViewNameTranslator(context);
// 初始化檢視轉換器
initViewResolvers(context);
// 初始化FlashMap 管理器
initFlashMapManager(context);
}
onRefresh方法何時被呼叫?
在此方法中打一個斷點,然後Debug模式啟動,然後觀察呼叫棧。
可以看到,最初是由refresh
方法呼叫的——finishRefresh()
,由finishRefresh()
釋出事件,然後觸發事件監聽,最終到了onRefresh
方法。
重點來看:
5.2.1 initHandlerMappings(context)
預設的配置:
initHandlerAdapters同理
***注意 多檔案上傳的元件(MultipartResolver)必須按照id註冊物件:
六,Handler方法細節刨析:
//org.springframework.web.servlet.DispatcherServlet#doDispatch
// 4 實際處理器處理請求,返回結果檢視物件
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
七,processDispatchResult方法
//跳轉檢視頁面,渲染檢視
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);