太厲害了!這是我見過最好的SpringMVC源碼分析 render方法完成渲染

一,DispatcherServlet繼承結構


二、SpringMvc請求處理的大致流程

2.1 Handler方法執行的時機

打斷點:

觀察調用棧:

doDispathch⽅法中的1064⾏代碼完成handler⽅法的調⽤

2.2 頁面渲染時機(打斷點並觀察調用棧)

2.3 doDispatch()方法核心步驟 (Springmvc處理請求的大致流程):

  1. 調用getHandler()獲取到能夠處理當前請求的執行鏈 HandlerExecutionChain(Handler + 攔截器)
  2. 調用getHandlerAdapter()獲取能夠執行Handler的適配器
  3. 適配器調用Handler執行ha.handle(),總會返回一個ModelAndView對象
  4. 調用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);

render方法完成渲染

作者:鄧曉暉
地址:https://www.cnblogs.com/isdxh/p/14115123.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章