從源碼角度瞭解SpringMVC的執行流程 SpringMVC的執行流程網上有很多帖子都有講解,流程圖和文字描述都很詳細,但是你如果沒有通過具體源碼自己走一遍流程,其實只是死記硬背。所以想開個帖子從源碼角度再梳理一遍SpringMVC的執行流程,加深印象。 [TOC] SpringMVC介紹 Spr ...
目錄
從源碼角度瞭解SpringMVC的執行流程
SpringMVC的執行流程網上有很多帖子都有講解,流程圖和文字描述都很詳細,但是你如果沒有通過具體源碼自己走一遍流程,其實只是死記硬背。所以想開個帖子從源碼角度再梳理一遍SpringMVC的執行流程,加深印象。
SpringMVC介紹
SpringMVC採用的是前端控制器(Front Controller) + 各個業務處理器(Controller)來處理請求的。前端控制器來響應所有請求,通過一定的調度規則找到具體負責處理的業務處理器,並將請求委派給具體的業務處理器去執行業務邏輯,業務處理器返回給前端控制器模型數據model,最後前端控制器將model交給視圖View進行渲染。
源碼分析思路
看源碼的同學可能往往會陷入一個怪圈,剛開始看可能還能看懂,等到一層一層點進去會越來越暈,讓自己陷入了太多的細節中,而這些細節其實對主要流程並沒有多大影響,然後就埋頭研究。之後不得不又從頭開始看,又讓自己陷入了另一個細節。其實看源碼開始時只是需要看一個大致的框架和思路,瞭解代碼的大致執行流程,千萬不要讓自己陷入到細節的泥潭中。所以本文是通過幾個關鍵的介面作為切入點來梳理SpringMVC的執行流程,如果我們把關鍵的介面弄懂了,也就瞭解了SpringMVC的執行流程。所以本文只是去瞭解介面功能,並不關註到具體的實現邏輯上。當我們把大體流程瞭解後,之後就只是各個擊破具體的實現類了。之後作者還會通過自己來實現這些介面來處理自己定義的請求,結合具體的例子來理解。
閱讀SpringMVC源碼給我最大的感觸有兩點:
- 開放封閉原則,SpringMVC中可擴展性很強,我們只需要實現具體的介面,然後將介面加入到容器中,就可以實現我們的擴展功能,不需要改任何代碼,對擴展開放。而且已有實現類中的關鍵方法都是用final修飾的,對修改關閉。
- 面向介面編程,所有重要的流程代碼,幾乎都是介面調用,而不是具體到某一個特定的類上面。
源碼解讀
註:源碼版本為 spring-webmvc-5.2.2.RELEASE.jar
幾個關鍵介面和類
HandlerMapping
public interface HandlerMapping {
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
HandlerMapping映射了請求與具體處理器的關係,可以理解為記憶體中有這樣一個記憶體數據 Map<request, Handler>,HandlerMapping就是根據請求從Map中找到Handler返回。HandlerExecutionChain只是將Handler和其對應的攔截器interceptors進行了包裝。
前文所說的調度規則,通過請求的 HttpServletRequest 獲取具體的請求處理類,將請求處理類包裝成 HandlerExecutionChain 返回。其中 HandlerExecutionChain中的Object handler就是具體的請求處理類。
public class HandlerExecutionChain {
private final Object handler;
@Nullable
private HandlerInterceptor[] interceptors;
@Nullable
private List<HandlerInterceptor> interceptorList;
}
我們現在通過請求找到了具體的處理類,那麼我們怎麼通過處理類去執行具體的方法呢?那麼就需要HandlerAdapter了。
HandlerAdapter
public interface HandlerAdapter {
/**
* 通過方法 supports 判斷適配器是否適配這種類型的Handler,返回true則代表適配。
*/
boolean supports(Object handler);
/**
* 如果適配則通過方法 handle 去讓 Object handler 執行具體的處理方法。
*/
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response
, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
HandlerAdapter 處理器的適配器,幫助前端控制器去執行處理器Handler中具體的處理業務,讓前端控制機不需要關註具體的執行細節,也就是說HandlerAdapter對前端控制機屏蔽了處理器執行的具體細節。
ModelAndView
public class ModelAndView {
/** View instance or view name String. */
@Nullable
private Object view;
/** Model Map. */
@Nullable
private ModelMap model;
對邏輯視圖名view和數據模型的封裝。
Object view為通常為String類型的邏輯視圖名。
ModelMap 為MVC中Model的角色,底層為Map類型。
ViewResolver
public interface ViewResolver {
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
解析邏輯視圖名viewName找到具體的View,前端控制器找到具體視圖View的嚮導。
View
public interface View {
void render(@Nullable Map<String, ?> model, HttpServletRequest request
, HttpServletResponse response) throws Exception;
}
調用render方法通過數據模型渲染視圖。
請求參數中的 Map<String, ?> model 在SpringMVC中扮演Model的角色。
比如我們想返回json格式的數據,那麼render方法邏輯就是將model轉為json格式輸出。
或者我們想返回jsp,那麼我們就可以model解析到具體的jsp頁面進行展示。
前端控制器 DispatcherServlet
在SpringMVC中,DispatcherServlet作為前端控制器,控制服務的具體執行流程,主要的執行流程代碼也都在這個類中。
下麵是DispatcherServlet簡化的源碼,只包含了重要的執行流程,保留了關鍵的執行代碼,讀者可以具體關鍵字進行搜索去找到具體的代碼行。
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request,
HttpServletResponse response) throws Exception {
HandlerExecutionChain mappedHandler = null;
ModelAndView mv = null;
mappedHandler = getHandler(processedRequest);
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
render(mv, request, response);
}
protected void render(ModelAndView mv, HttpServletRequest request
, HttpServletResponse response) throws Exception {
String viewName = mv.getViewName();
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
view.render(mv.getModelInternal(), request, response);
}
代碼流程:
- 獲取處理器:HandlerMapping通過HttpServletRequest請求找到具體的Handler
- 獲取處理器對應的適配器:通過Handler找到具體的HandlerAdapter
- 調用處理器的處理邏輯:HandlerAdapter調用Handler執行具體的處理邏輯,返回ModelAndView
- 解析視圖:ViewResolver通過ModelAndView中的邏輯視圖名找到具體的View。
- 渲染視圖:View將數據模型進行渲染。
獲取處理器
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
SpringMVC在啟動的時候會掃描所有實現了HandlerMapping介面的類,並將這些類加入到容器中。
獲取處理器其實就是迴圈實現了HandlerMapping的類,調用getHandler()方法,找到了就停止並返回。
每種類型的Handler都有各自對應的HandlerMapping。比如SpirngMVC中預設的處理器為 Controller,與之對於的HandlerMapping為RequestMappingHandlerMapping。
獲取處理器的適配器
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
}
邏輯和獲取處理器一樣,判斷邏輯就是前面提過的,只要supports方法返回true,則代表適配這個Handler。
同理也是一種Handler應該有與之對應的HandlerAdapter。與Controller對應的為RequestMappingHandlerAdapter。
所以如果我們要編寫自定義的處理器。那麼我們需要自己的Handler類和與之對於的HandlerMapping和HandlerAdapter。
解析視圖
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
仍然是同樣的邏輯,所有的代碼都是面向介面來開發的。
SpringMVC支持各種各樣的視圖渲染,如JSP、json、freemarker、thymeleaf。ViewResolver就是這些視圖的嚮導,它告訴SpringMVC需要通過什麼方式去找到具體的視圖View。
每種視圖View都有自己對應的視圖解析器,例如FreeMarkerView對應的視圖解析器為FreeMarkerViewResolver。
視圖渲染
其實就一句代碼。
view.render(mv.getModelInternal(), request, response);
視圖渲染,作者剛開始看到這個詞,覺得好高大上。其實就是讓數據模型model應以什麼樣的方式來展示。
如果你想將model以json格式返回,那麼你就去實現View介面,把model轉為json格式,然後寫入到響應類的輸出流即可。
ServletOutputStream out = response.getOutputStream();
baos.writeTo(json);
out.flush();
結語
本文只是通過幾個重要的介面來描述SpringMVC的執行流程,沒有具體分析實現類的邏輯。也想在這裡分享下自己看源碼的心得體會。看源碼時千萬不要讓自己陷入過深的業務邏輯中去,先看主要執行流程,重要的介面,比如以debug的方式先預覽下執行的方法棧,根據方法棧去定位。如果有哪些地方有誤或者有不同的理解,還請不吝賜教。