天天看點

SpringMVC源碼解析之RequestMappingHandlerAdapter(下)總結

handleInternal

使用給定的處理器方法處理請求。

AbstractHandlerMethodAdapter

中定義的 protected 抽象方法,專門由該子類實作。

開始處理請求,傳回一個ModelAndView。

@Override
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) {
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
        }
        else {
            // No HttpSession available -> no mutex necessary
            mav = invokeHandlerMethod(request, response, handlerMethod);
        }
    } else {
        // No synchronization on session demanded at all...
        mav = invokeHandlerMethod(request, response, handlerMethod);
    }

    // 處理Cache-Control請求頭(若你沒有set
    if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
        if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
            applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
        } else {
            prepareResponse(response);
        }
    }

    return mav;
}
      

剩下的便是本擴充卡最為重要的一個方法:invokeHandlerMethod():

// 它的作用就是執行目标的HandlerMethod,然後傳回一個ModelAndView 
    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        // 注意:此處隻有try-finally 哦
        // 因為invocableMethod.invokeAndHandle(webRequest, mavContainer)是可能會抛出異常的(交給全局異常處理)
        try {
            // 最終建立的是一個ServletRequestDataBinderFactory,持有所有@InitBinder的method方法們
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            // 建立一個ModelFactory,@ModelAttribute啥的方法就會被引用進來
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            // 把HandlerMethod包裝為ServletInvocableHandlerMethod,具有invoke執行的能力喽
            // 下面這幾部便是一直給invocableMethod的各大屬性指派~~~
            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();
            // 把上個request裡的值放進來到本request裡
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            // model工廠:把它裡面的Model值放進mavContainer容器内(此處@ModelAttribute/@SessionAttribute啥的生效)
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);

            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

            // 它不管是不是異步請求都先用AsyncWebRequest 包裝了一下,但是若是同步請求
            // asyncManager.hasConcurrentResult()肯定是為false的~~~
            if (asyncManager.hasConcurrentResult()) {
                Object result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                LogFormatUtils.traceDebug(logger, traceOn -> {
                    String formatted = LogFormatUtils.formatValue(result, !traceOn);
                    return "Resume with async result [" + formatted + "]";
                });
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

            // 此處其實就是調用ServletInvocableHandlerMethod#invokeAndHandle()方法喽
            // 關于它你可以來這裡:https://fangshixiang.blog.csdn.net/article/details/98385163
            // 注意哦:任何HandlerMethod執行完後都是把結果放在了mavContainer裡(它可能有Model,可能有View,可能啥都木有~~)
            // 是以最後的getModelAndView()又得一看
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }

            return getModelAndView(mavContainer, modelFactory, webRequest);
        } finally {
            webRequest.requestCompleted();
        }
    }
任何處理器執行完後,最終傳回的的都是一個ModelAndView對象,這得益于getModelAndView()這個方法的适配處理:

    // @Nullable:表示它傳回的可以是個null哦~(若木有視圖,就直接不會render啦~因為response已經寫入過值了)
    @Nullable
    private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
    
        // 把session裡面的内容寫入
        modelFactory.updateModel(webRequest, mavContainer);
        // Tips:若已經被處理過,那就傳回null喽~~(比如若是@ResponseBody這種,這裡就是true)
        if (mavContainer.isRequestHandled()) {
            return null;
        }
            
        // 通過View、Model、Status構造出一個ModelAndView,最終就可以完成渲染了
        ModelMap model = mavContainer.getModel();
        ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
        if (!mavContainer.isViewReference()) { // 是否是String類型
            mav.setView((View) mavContainer.getView());
        }

        // 對重定向RedirectAttributes參數的支援(兩個請求之間傳遞參數,使用的是ATTRIBUTE)
        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;
    }      

執行完HandlerMethod後得到一個ModelAndView,它可能是null(比如已被處理過),那最終交給DispatcherServlet就沒有後續處理了,否則會做視圖渲染:render()。

Spring MVC預設裝配了哪些HandlerAdapter呢?

開啟@EnableWebMvc:

總結

RequestMappingHandlerAdapter作為HandlerAdapter适配模式的實作,由于@RequestMapping成為了使用Spring MVC的幾乎唯一選擇,是以它成為了實際意義上的标準實作