目錄
3.3 DispatcherServlet 分發請求(核心處理邏輯)
3.4 HandlerAdapter 調用流程(RequestMappingHandlerAdapter)—— 調用處理器方法
3.5 HandlerAdapter 調用流程(RequestMappingHandlerAdapter)—— 處理器返回處理結果
寫文章不易,時序圖均爲自研,轉載請標明出處。
同時,如果你喜歡我的文章,請關注我,讓我們一起進步。
一、概述
這篇博文的主要內容就是根據源碼來解析 SpringMVC 中對於請求的處理流程,因爲 SpringMVC 中對於請求的處理流程非常的繁雜,代碼量也是非常的多,爲了整個解析的過程清晰一點,我儘量用完全代碼註釋的方式來完成這篇博文,同時爲了便於大家理解整個流程,我在開頭提供了第一版的流程圖,在源碼解析完成後提供了方法調用的時序圖,並且在最後的內容總結中梳理了整個流程的主體邏輯,注意這裏是主體,因爲如果將每一個部分都展開來將實在太多了,下面開始正文。
二、流程圖(第一版)
三、源碼分析
3.1 DispatcherServlet 接收請求
// FrameworkServlet.class
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 獲取請求方法
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
// 如果請求方法爲 PATCH 或者爲空則調用 processRequest 進行處理
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
// 否則直接調用父類 HttpServlet 的 service 方法進行處理
super.service(request, response);
}
}
// FrameworkServlet.class
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 當請求方法爲 GET 時 HttpServlet 調用到子類 FrameworkServlet 中的 processRequest 方法進行處理
processRequest(request, response);
}
3.2 DispatcherServlet 處理請求
// FrameworkServlet.class
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 獲取與當前線程關聯的 LocaleContext(主要用於處理完請求後通過 resetContextHolders 恢復狀態)
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 爲給定請求構建 LocaleContext ,並將請求的主要語言環境設置爲當前語言環境
LocaleContext localeContext = buildLocaleContext(request);
// 獲取綁定到當前線程的 RequestAttributes(主要用於處理完請求後通過 resetContextHolders 恢復狀態)
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 考慮到預先綁定的屬性(及其類型),爲給定的請求構建 ServletRequestAttributes(可能還包含對響應的引用)
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
// 獲取當前請求的 WebAsyncManager,如果找不到則創建並將其與請求關聯
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
// 在給定鍵(ServletName)下注冊一個 CallableProcessingInterceptor
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 初始化 ContextHolder(LocaleContextHolder 和 RequestContextHolder)
initContextHolders(request, localeContext, requestAttributes);
try {
// 通過 doService 方法處理請求
doService(request, response);
}
catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
// 恢復當前線程 ContextHolder 的初始狀態
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
// 初始化 ContextHolder
private void initContextHolders(HttpServletRequest request, @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
if (localeContext != null) {
LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
}
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
}
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// 在 include 的情況下,保留請求屬性的快照,以便能夠在 include 之後恢復原始屬性
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// 爲 Request 請求添加 Spring Web 容器引用以及各種解析器
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());
// 爲 Request 請求添加 FlashMap 和 flashMapManager 屬性值(屬性作用在上面介紹過)
if (this.flashMapManager != null) {
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 進行請求分發工作
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// 在 include 的情況下,恢復原始屬性快照
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
3.3 DispatcherServlet 分發請求(核心處理邏輯)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// 獲取當前線程的 WebAsyncManager
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 如果存在 Multipart 解析器且當前請求尚未被解析,則將請求轉換爲 Multipart 請求
// 否則如果未設置 Multipart 解析器則直接使用原始請求
processedRequest = checkMultipart(request);
// 判斷原始請求是否被轉換成了 Multipart 請求
multipartRequestParsed = (processedRequest != request);
// 步驟一:獲取當前請求的處理器(handler)
mappedHandler = getHandler(processedRequest);
// 如果當前請求不存在處理器則設置適當的 HTTP 響應狀態然後直接返回
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 步驟二:獲取當前請求的處理器適配器(HandlerAdapter)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 如果處理程序支持,則處理最後修改的標頭(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;
}
}
// 步驟三:applyPreHandle 方法會調用已經被註冊攔截器的 preHandle 方法
// 如果執行鏈應該繼續執行下一個攔截器或處理器則返回 true
// 反之當該方法返回 false 的時候 DispatcherServlet 假定此攔截器已經處理了響應本身
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 步驟四:實際調用處理器對請求進行處理,返回一個 ModelAndView 對象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 步驟五:調用攔截器(HandlerInterceptor)的 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 步驟六:處理 handler 選擇和 handler 調用的結果
// 該結果可以是 ModelAndView 或要解析爲 ModelAndView 的異常
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()) {
// 代替攔截器中的 postHandle 和 afterCompletion 方法
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 清理 multipart 請求使用的所有資源
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
3.4 HandlerAdapter 調用流程(RequestMappingHandlerAdapter)—— 調用處理器方法
// AbstractHandlerMethodAdapter.class
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
// RequestMappingHandlerAdapter.class
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// 如果需要同步,則在同步塊中執行 invokeHandlerMethod
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
// 這裏使用了 Session 互斥鎖
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 如果沒有可用的 HttpSession 那麼就沒有必要使用 Session 互斥鎖
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 如果根本不需要 Session 上的同步,那麼直接調用 invokeHandlerMethod 方法
mav = invokeHandlerMethod(request, response, handlerMethod);
}
// 處理響應緩存
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 將請求信息和響應信息包裝爲 ServletWebRequest 實例對象
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 創建可被調用的 ServletInvocableHandlerMethod 對象來包裝原始的 HandlerMethod
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 如果存在參數解析器則設置參數解析器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
// 如果存在返回值處理器則設置返回值處理器
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 創建數據和視圖容器並進行初始化工作
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 使用異步請求處理方法擴展 NativeWebRequest
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
// 創建用於管理異步請求處理的中央類 WebAsyncManager 並進行屬性設置
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
// 配置 AsyncTaskExecutor 用於 startCallableProcessing 方法的併發執行
asyncManager.setTaskExecutor(this.taskExecutor);
// 配置 AsyncWebRequest
asyncManager.setAsyncWebRequest(asyncWebRequest);
// 註冊回調攔截器 CallableProcessingInterceptor
asyncManager.registerCallableInterceptors(this.callableInterceptors);
// 註冊 DeferredResultProcessingInterceptor
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
// 如果結果值作爲併發處理的結果而存在
if (asyncManager.hasConcurrentResult()) {
// 獲取併發處理的結果
Object result = asyncManager.getConcurrentResult();
// 獲取併發處理開始時保存的其它處理上下文
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
// 清除 asyncManager 中的 concurrentResult 和 concurrentResultContext
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
// 創建一個嵌套的 ServletInvocableHandlerMethod 子類 ConcurrentResultHandlerMethod
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 調用處理請求的方法並將請求包裝對象(ServletWebRequest)和數據視圖對象(ModelAndViewContainer)傳入
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 調用方法並返回執行結果
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 設置響應狀態
setResponseStatus(webRequest);
// 省略返回值處理邏輯 ...
}
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 獲取方法參數
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
// 實際調用方法處
return doInvoke(args);
}
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 獲取到橋接方法並最終通過反射調用方法
return getBridgedMethod().invoke(getBean(), args);
}
...
}
@RequestMapping(value = "/to_login", method = RequestMethod.GET)
public String toLogin(){
return "user_login";
}
3.5 HandlerAdapter 調用流程(RequestMappingHandlerAdapter)—— 處理器返回處理結果
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 獲取調用後的返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 設置相應狀態
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 處理返回值
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
throw ex;
}
}
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 選擇返回值處理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
// 判斷是否爲異步返回值
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
// returnValueHandlers 中所包含的 ReturnValueHandler 如下圖
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
// 最簡單的情況即我們只返回了一個視圖名稱,此時返回 ViewNameMethodReturnValueHandler
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
// ViewNameMethodReturnValueHandler.java
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}
// ViewNameMethodReturnValueHandler.java
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
// 設置視圖名稱,該名稱後面會由 DispatcherServlet 通過 ViewResolver 解析
mavContainer.setViewName(viewName);
// 判斷是否爲重定向視圖名稱(即 viewName.startsWith("redirect:"))
if (isRedirectViewName(viewName)) {
// 如果需要重定向則做好重定向標記
mavContainer.setRedirectModelScenario(true);
}
}
else if (returnValue != null){
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
...
// 進行請求處理器方法調用
invocableMethod.invokeAndHandle(webRequest, mavContainer);
// 調用結束後返回到這裏
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 獲取數據和視圖
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
// 將 model 中的屬性列表添加到 session 的 SessionAttributes 中(級別提升)
modelFactory.updateModel(webRequest, mavContainer);
// 如果請求已經被處理了則直接返回
if (mavContainer.isRequestHandled()) {
return null;
}
// 從返回值容器中獲取數據
ModelMap model = mavContainer.getModel();
// 將處理結果封裝到 ModelAndView 對象中
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
// isViewReference 直接判斷 mavContainer 的 view 屬性值是否爲 String 類型
// 如果 mavContainer 保存的 view 是一個 View 對象而非 String 則將其直接保存到 ModelAndView 對象中
mav.setView((View) mavContainer.getView());
}
// 重定向邏輯
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
...
else {
// 調用處理器方法處理請求
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
protected final void prepareResponse(HttpServletResponse response) {
if (this.cacheControl != null) {
applyCacheControl(response, this.cacheControl);
}
else {
// 應用給定的緩存秒數並生成相應的 HTTP 報頭
// 即在爲正數的情況下允許緩存給定的秒數,如果給定值爲 0 則阻止緩存且不執行其他任何操作
applyCacheSeconds(response, this.cacheSeconds);
}
if (this.varyByRequestHeaders != null) {
for (String value : getVaryRequestHeadersToAdd(response, this.varyByRequestHeaders)) {
response.addHeader("Vary", value);
}
}
}
3.6 DispatcherServlet 處理分發結果
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
// 步驟四:調用處理器方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 步驟五:調用攔截器的 postHandle 方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
// 步驟六:處理分發後的結果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
finally {
// 當併發處理時執行的邏輯
if (asyncManager.isConcurrentHandlingStarted()) {
// 替代攔截器中的 postHandle 和 afterCompletion 方法
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 清理多部分請求使用的所有資源
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// 判斷是否存在視圖需要被渲染
if (mv != null && !mv.wasCleared()) {
// 渲染視圖
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}
if (mappedHandler != null) {
// 視圖渲染完成後執行 HandlerInterceptor 攔截器的 afterCompletion 方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 獲取請求的區域設置,並將其應用於響應
Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 通過視圖名解析視圖
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// 如果 ModelAndView 對象包含實際的視圖對象則直接獲取
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() + "'");
}
}
try {
// 委託給視圖對象進行呈現
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
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) {
// 調用 ViewResolver 進行視圖解析,解析後的 View 對象結構如下圖
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
// 返回成功解析後的視圖
return view;
}
}
}
return null;
}
四、時序圖(非完全通用版)
@RequestMapping(value = "/to_login", method = RequestMethod.GET)
public String toLogin(){
return "user_login";
}
五、內容總結
5.1 HandlerInterceptor 的調用時機
(1)preHandle:DIspatcherServlet 的 doDispatch 方法中,在執行 HandlerAdapter 的 handle 方法(調用處理器處理請求)之前,通過 HandlerExecutionChain 的 applyPreHandle 方法進行調用;
(2)postHandle:DispatcherServlet 的 doDIspatch 方法中,在執行 HandlerAdapter 的 handle 方法(調用處理器處理請求)之後,通過 HandlerExecutionChain 的 applyPostHandle 方法進行調用;
(3)afterCompletion:在 DispatcherServ 的 processDispatchResult 方法中,在執行完 render 視圖渲染後,通過 HandlerExecutionChain 的 triggerAfterCompletion 方法被調用;
5.2 SpringMVC 處理請求流程總結(通用版本)
(1)請求到達服務端時首先調用 FrameworkServlet 中重寫 HttpServlet 中的 service 方法,如果請求類型爲 PATCH 或者爲空則直接調用 FrameworkServlet 中的 processRequest 方法進行處理,否則將調用父類 HttpServlet 中的 service 方法;
(2)當執行了 HttpServlet 的 service 方法後,又會調用到 FrameworkServlet 中重寫 HttpServlet 中的 doGet 或 doPost 方法,假設當我們的請求爲 GET 類型時,會調用到 FrameworkServlet 中的 doGet 方法,在 doGet 方法中又會調用到它自身的 processRequest 方法,並將請求的 request 和 response 作爲參數傳入;
(3)在 processRequest 方法中會先進行一些地區和主體的屬性獲取和設置,然後比較核心的就是調用到 doService 方法來進行業務邏輯的處理;
(4)在 doService 方法中主要進行的就是公開 DIspatcherServlet 中的一些指定的屬性(將 localeResolver 位置解析器和 themeResolver 主題解析器或一些其它屬性設置到 request 的 attribute 中,使得它們可以通過 request 在全局被訪問),還有就是通過調用 doDispatch 方法來進行實際的分發處理;
(5)在 doDispatch 方法中的邏輯比較複雜,所以我們再展開來講:
5.1 首先通過 getHandler 方法從 HandlerMapping 中獲取 HandlerExecutionChain,即處理器執行鏈(這個鏈中包括了 Handler 處理器和 HandlerInterceptor 攔截器)。需要注意的這裏一般存在三種 HandlerMapping,我們一般最常用的也就是 RequestMappingHandlerMapping(它是從 @Controller 類中的類型和方法級 @RequestMapping 註釋創建 RequestMappingInfo 實例),其餘的兩種因爲篇幅原因不在這裏贅述;
5.2 然後在通過 getHandlerAdapter 方法獲取 Handler 對應的 HandlerAdapter 處理器適配器(每一種 Handler 都對應着不同的 Adapter,比如我們上面提到的那個通過 RequestMappingHandlerMapping 獲取到的 Handler 所對應的適配器是 RequestMappingHandlerAdapter);
5.3 調用上面 getHandler 方法所返回的 HandlerExecutionChain 中 HandlerInterceptor 攔截器的 applyPreHandle 方法;
5.4 之後通過調用 HandlerAdapter 的 handle 方法來執行請求所對應的方法(這裏的邏輯很複雜,而且不同的 Adapter 有不同的實現邏輯,一般最後是通過反射來實現方法調用的),並接收方法所返回的 ModelAndView 對象;
5.5 調用上面 getHandler 方法所返回的 HandlerExecutionChain 中 HandlerInterceptor 攔截器的 applyPostHandle 方法;
5.6 調用 processDispatchResult 方法來處理分發後的結果,這裏的結果包括 handle 方法調用後返回的 ModelAndView 和在上面調用過程中產生的需要轉換爲 ModelAndView 的 Exception(需要返回給客戶端的 Exception),然後會判斷當前是否需要進行視圖渲染,如果需要則調用 render 方法來進行視圖渲染;
5.7 我們現在假設正常情況下存在視圖需要被渲染,則在 render 方法又會判斷當前的 ModelAndView 中的 ViewName 是否爲空,如果不爲空證明我們需要去解析視圖名稱(resolveViewName)得到 View 對象,然後調用 View 對象的 render 方法來讓指定的 ViewResolver 來對視圖進行解析,但如果 ViewName 爲空,那麼我們就直接嘗試從 ModelAndView 對象中獲取 View 對象;
5.8 在 processDispatchResult 方法中完成了 render 視圖渲染後,會調用 HandlerExecutionChain 中 HandlerInterceptor 攔截器的 afterCompletion 方法。當其返回到 doDispatch 方法中時,在其 catch 方法中也會執行(保證在異常情況下 afterCompletion 方法也能夠被正常執行);
(6)當完成 doDispatch 方法後就會進行層層返回,直到最後將響應發送給客戶端;
5.3 心得體會
這篇博文從開始計劃,到後面的先查詢資料大概理解,然後一步一步的源碼調試來捋清業務邏輯大概是有三四天的時間,整個博文花費的時間可能要幾十個小時,很多時候就是自己一不小心就陷入了局部的代碼邏輯,導致自己的源碼越讀越多,越讀越亂。所以中間經過了好幾次的調整,但其實到最後的話自己對於源碼分析階段也不是特別的滿意,因爲涉及的內容太多了,導致自己不能很好地組織好整篇博文的邏輯,所以在前面和後面分別加了流程圖、時序圖和總結來彌補一下。
在這個這部分源碼閱讀的過程中自己也遇到了很多新的技術,也開始慢慢的瞭解了 SpringMVC 中一些組件的作用,所以在這篇博文完成後,自己會在後面的幾天對這篇博文中一帶而過的很多技術進行詳細的剖析,來加深自己對於 SpringMVC 的理解。
若不給自己設限,則人生中就沒有限制你發揮的藩籬。