SpringMVC執行流程及原始碼解析
在SpringMVC中主要是圍繞著DispatcherServlet來設計,可以把它當做指揮中心。這裡先說明一下SpringMVC文件給出的執行流程,然後是我們稍微具體的執行流程,最後是流程大致的原始碼跟蹤。關於很很很詳細的原始碼解析,這裡暫先不做。
官方文件中的流程
首先看下SpringMVC文件上給的流程圖:
這張圖片給了我們大概的執行流程:
- 使用者請求首先發送到前端控制器DispatcherServlet,DispatcherServlet根據請求的資訊來決定使用哪個頁面控制器Controller(也就是我們通常編寫的Controller)來處理該請求。找到控制器之後,DispatcherServlet將請求委託給控制器去處理。
- 接下來頁面控制器開始處理使用者請求,頁面控制器會根據請求資訊進行處理,呼叫業務層等等,處理完成之後,會把結果封裝成一個ModelAndView返回給DispatcherServlet。
- 前端控制器DispatcherServlet接到頁面控制器的返回結果後,根據返回的檢視名選擇相應的試圖模板,並根據返回的資料進行渲染。
- 最後前端控制器DispatcherServlet將結果返回給使用者。
更具體的流程
上面只是總體流程,接下來我們稍微深入一點,看下更具體的流程,這裡沒有圖,只有步驟解析:
- 使用者請求傳送到前端控制器DispatcherServlet。
- 前端控制器DispatcherServlet接收到請求後,DispatcherServlet會使用HandlerMapping來處理,HandlerMapping會查詢到具體進行處理請求的Handler物件。
- HandlerMapping找到對應的Handler之後,並不是返回一個Handler原始物件,而是一個Handler執行鏈,在這個執行鏈中包括了攔截器和處理請求的Handler。HandlerMapping返回一個執行鏈給DispatcherServlet。
- DispatcherServlet接收到執行鏈之後,會呼叫Handler介面卡去執行Handler。
- Handler介面卡執行完成Handler(也就是我們寫的Controller)之後會得到一個ModelAndView,並返回給DispatcherServlet。
- DispatcherServlet接收到Handler介面卡返回的ModelAndView之後,會根據其中的檢視名呼叫檢視解析器。
- 檢視解析器根據邏輯檢視名解析成一個真正的View檢視,並返回給DispatcherServlet。
- DispatcherServlet接收到檢視之後,會根據上面的ModelAndView中的model來進行檢視渲染完成之後,DispatcherServlet就可以將結果返回給使用者了。
原始碼
DispatcherServlet是一個Servlet,我們知道在Servlet在處理一個請求的時候會交給service方法進行處理,這裡也不例外,DispatcherServlet繼承了FrameworkServlet,首先進入FrameworkServlet的service方法:
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//請求方法
String method = request.getMethod();
//PATCH方法單獨處理
if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
processRequest(request, response);
}
else {//其他的請求型別的方法經由父類,也就是HttpServlet處理
super.service(request, response);
}
}
HttpServlet中會根據請求型別的不同分別呼叫doGet或者doPost等方法,FrameworkServlet中已經重寫了這些方法,在這些方法中會呼叫processRequest進行處理,在processRequest中會呼叫doService方法,這個doService方法就是在DispatcherServlet中實現的。下面就看下DispatcherServlet中的doService方法的實現。
請求到達DispatcherServlet doService方法:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//給request中的屬性做一份快照
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
//如果我們沒有配置類似本地化或者主題的處理器之類的
//SpringMVC會使用預設的值
//預設配置檔案是DispatcherServlet.properties
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
//開始處理
doDispatch(request, response);
}
finally {
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
DispatcherServlet開始真正的處理, doDispatch方法 :
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//SpringMVC中非同步請求的相關知識,暫先不解釋
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//先檢查是不是Multipart型別的,比如上傳等
//如果是Multipart型別的,則轉換為MultipartHttpServletRequest型別
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//獲取當前請求的Handler
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//獲取當前請求的Handler介面卡
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 對於header中last-modified的處理
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;
}
}
//攔截器的preHandle方法進行處理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
//真正呼叫Handler的地方
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
//處理成預設檢視名,就是新增字首和字尾等
applyDefaultViewName(request, mv);
//攔截器postHandle方法進行處理
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//處理最後的結果,渲染之類的都在這裡
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
可以看到大概的步驟還是按照我們上面分析的走的。
查詢請求對應的Handler物件
對應著這句程式碼mappedHandler = getHandler(processedRequest, false);,看下具體的getHandler方法:
protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
return getHandler(request);
}
繼續往下看getHandler:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//遍歷所有的handlerMappings進行處理
//handlerMappings是在啟動的時候預先註冊好的
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
繼續往下看getHandler,在AbstractHandlerMapping類中:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//根據request獲取handler
Object handler = getHandlerInternal(request);
if (handler == null) {
//如果沒有找到就使用預設的handler
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
//如果Handler是String,表明是一個bean名稱
//需要超照對應bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
//封裝Handler執行鏈
return getHandlerExecutionChain(handler, request);
}
根據requrst獲取handler
首先看下根據requrst獲取handler步驟getHandlerInternal方法,在AbstractHandlerMethodMapping中:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//獲取request中的url,用來匹配handler
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//根據路徑尋找Handler
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//根據handlerMethod中的bean來例項化Handler並新增進HandlerMethod
return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
}
看下根據路徑尋找handler的方法lookupHandlerMethod:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//直接匹配
List<T> directPathMatches = this.urlMap.get(lookupPath);
//如果有匹配的,就新增進匹配列表中
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
//還沒有匹配的,就遍歷所有的處理方法查詢
if (matches.isEmpty()) {
// No choice but to go through all mappings
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
}
//找到了匹配的
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
//排序之後,獲取第一個
Match bestMatch = matches.get(0);
//如果有多個匹配的,會找到第二個最合適的進行比較一下
if (matches.size() > 1) {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException(
"Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
m1 + ", " + m2 + "}");
}
}
//設定request引數
handleMatch(bestMatch.mapping, lookupPath, request);
//返回匹配的url的處理的方法
return bestMatch.handlerMethod;
}
else {//最後還沒有找到,返回null
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}
獲取預設Handler
如果上面沒有獲取到Handler,就會獲取預設的Handler。如果還獲取不到就返回null。
處理String型別的Handler
如果上面處理完的Handler是String型別的,就會根據這個handlerName獲取bean。
封裝Handler執行鏈
上面獲取完Handler,就開始封裝執行鏈了,就是將我們配置的攔截器加入到執行鏈中去,getHandlerExecutionChain:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//如果當前Handler不是執行鏈型別,就使用一個新的執行鏈例項封裝起來
HandlerExecutionChain chain =
(handler instanceof HandlerExecutionChain) ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
//先獲取適配型別的攔截器新增進去攔截器鏈
chain.addInterceptors(getAdaptedInterceptors());
//當前的url
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
//遍歷攔截器,找到跟當前url對應的,新增進執行鏈中去
for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}
獲取對應請求的Handler介面卡
getHandlerAdapter:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//遍歷所有的HandlerAdapter,找到和當前Handler匹配的就返回
//我們這裡會匹配到RequestMappingHandlerAdapter
for (HandlerAdapter ha : this.handlerAdapters) {
if (ha.supports(handler)) {
return ha;
}
}
}
快取的處理
也就是對last-modified的處理
執行攔截器的preHandle方法
就是遍歷所有的我們定義的interceptor,執行preHandle方法
使用Handler介面卡執行當前的Handler
ha.handle執行當前Handler,我們這裡使用的是RequestMappingHandlerAdapter,首先會進入AbstractHandlerMethodAdapter的handle方法:
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
handleInternal方法,在RequestMappingHandlerAdapter中:
protected final ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
}
else {
// Uses configured default cacheSeconds setting.
checkAndPrepare(request, response, true);
}
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandleMethod(request, response, handlerMethod);
}
}
}
//執行方法,封裝ModelAndView
return invokeHandleMethod(request, response, handlerMethod);
}
組裝預設檢視名稱
字首和字尾名都加上
執行攔截器的postHandle方法
遍歷intercepter的postHandle方法。
處理最後的結果,渲染之類的
processDispatchResult方法:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
//渲染
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
重點看下render方法,進行渲染:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
//設定本地化
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
//解析檢視名,得到檢視
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
//委託給檢視進行渲染
view.render(mv.getModelInternal(), request, response);
}
view.render就是進行檢視的渲染,然後跳轉頁面等處理。
到這裡大概的流程就走完了。其中涉及到的東西還有很多,暫先不做詳細處理。