1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        面試官:你畫的SpringMVC請(qǐng)求處理過程是從網(wǎng)上抄的吧?

        共 27325字,需瀏覽 55分鐘

         ·

        2020-08-24 11:17

        程序員的成長之路
        互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
        關(guān)注


        閱讀本文大概需要 16?分鐘。

        作者:Sicimike
        原文:blog.csdn.net/Baisitao_/article/details/107471719

        前言

        SpringMVC請(qǐng)求處理相信大家都很熟悉了,本篇主要是基于SpringMVC處理請(qǐng)求的流程來閱讀并調(diào)試源碼,以及解決幾個(gè)僅靠流程圖無法解釋的問題。
        本篇使用的Spring版本為5.2.2.RELEASE

        九大組件

        SpringMVC幾乎所有的功能都由九大組件來完成,所以明白九大組件的作用,對(duì)于學(xué)習(xí)SpringMVC來說非常重要。

        /**?文件上傳解析器?*/
        private?MultipartResolver?multipartResolver;

        /**?區(qū)域解析器,用于國際化?*/
        private?LocaleResolver?localeResolver;

        /**?主題解析器?*/
        private?ThemeResolver?themeResolver;

        /**?Handler映射信息?*/
        private?List?handlerMappings;

        /**?Handler適配器*/
        private?List?handlerAdapters;

        /**?Handler執(zhí)行異常解析器?*/
        private?List?handlerExceptionResolvers;

        /**?請(qǐng)求到視圖的轉(zhuǎn)換器?*/
        private?RequestToViewNameTranslator?viewNameTranslator;

        /**?SpringMVC允許重定向時(shí)攜帶參數(shù),存在session中,用完就銷毀,所以叫FlashMap?*/
        private?FlashMapManager?flashMapManager;

        /**?視圖解析器?*/
        private?List?viewResolvers;
        1234567891011121314151617181920212223242526

        • HandlerMapping:Handler映射信息,根據(jù)請(qǐng)求攜帶的url信息查找處理器(Handler)。每個(gè)請(qǐng)求都需要對(duì)應(yīng)的Handler處理。
        • HandlerAdapter:Handler適配器,SpringMVC沒有直接調(diào)用處理器(Handler),而是通過HandlerAdapter來調(diào)用,主要是為了統(tǒng)一Handler的調(diào)用方式
        • ViewResolver:視圖解析器,用來將字符串類型的視圖名稱解析為View類型的視圖。ViewResolver需要找到渲染所用的模板和所用的技術(shù)(也就是視圖的類型)進(jìn)行渲染,具體的渲染過程則交由不同的視圖自己完成。
        • MultipartResolver:文件上傳解析器,主要用來處理文件上傳請(qǐng)求
        • HandlerExceptionResolver:Handler執(zhí)行異常解析器,用來對(duì)異常進(jìn)行統(tǒng)一處理
        • RequestToViewNameTranslator:請(qǐng)求到視圖的轉(zhuǎn)換器
        • LocaleResolver:區(qū)域解析器,用于支持國際化
        • FlashMapManager:SpringMVC允許重定向時(shí)攜帶參數(shù),存在session中,用完就銷毀,所以叫FlashMap
        • ThemeResolver:主題解析器,用于支持不同的主題 九大組件中最重的的前三個(gè),HandlerMapping、HandlerAdapter和ViewResolver,因?yàn)檫@是閱讀源碼時(shí),避不開的三個(gè)組件。

        調(diào)試準(zhǔn)備

        搭建一個(gè)基本的Spring web項(xiàng)目即可
        Controller部分

        @Controller
        public?class?IndexController?{

        ????@RequestMapping("/index/home")
        ????public?String?home(String?id,?Student?student,?@RequestParam("code")?String?code)?{
        ????????System.out.println(student.getName());
        ????????return?"index";
        ????}

        ????@ResponseBody
        ????@RequestMapping("/index/list")
        ????public?String?list()?{
        ????????return?"success";
        ????}
        }

        Entity部分

        public?class?Student?{

        ????private?String?name;
        ????private?Integer?gender;

        ???//?getter、setter
        }

        還是那句話,Spring源碼非常龐大,不能只見樹木不見森林,需要有針對(duì)性的閱讀,所以本篇只需要關(guān)注主體流程即可。

        核心方法

        我們都知道,SpringMVC有一個(gè)用來分發(fā)請(qǐng)求的前端控制器DispatcherServlet,其中用來處理請(qǐng)求的方法就是doService,該方法定義如下
        doService

        /**
        ?*?Exposes?the?DispatcherServlet-specific?request?attributes?and?delegates?to?{@link?#doDispatch}
        ?*?for?the?actual?dispatching.
        ?*/
        @Override
        protected?void?doService(HttpServletRequest?request,?HttpServletResponse?response)?throws?Exception?{
        ?logRequest(request);

        ?//?Keep?a?snapshot?of?the?request?attributes?in?case?of?an?include,
        ?//?to?be?able?to?restore?the?original?attributes?after?the?include.
        ?Map?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));
        ???}
        ??}
        ?}

        ?//?Make?framework?objects?available?to?handlers?and?view?objects.
        ?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());

        ?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?{
        ??//?真正執(zhí)行的方法
        ??doDispatch(request,?response);
        ?}
        ?finally?{
        ??if?(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted())?{
        ???//?Restore?the?original?attribute?snapshot,?in?case?of?an?include.
        ???if?(attributesSnapshot?!=?null)?{
        ????restoreAttributesAfterInclude(request,?attributesSnapshot);
        ???}
        ??}
        ?}
        }

        doDispatch
        doDispatch是doService中真正用來處理請(qǐng)求的方法

        /**
        ?*?實(shí)際處理請(qǐng)求的方法
        ?*/
        protected?void?doDispatch(HttpServletRequest?request,?HttpServletResponse?response)?throws?Exception?{
        ?HttpServletRequest?processedRequest?=?request;
        ?HandlerExecutionChain?mappedHandler?=?null;
        ?boolean?multipartRequestParsed?=?false;

        ?WebAsyncManager?asyncManager?=?WebAsyncUtils.getAsyncManager(request);

        ?try?{
        ??ModelAndView?mv?=?null;
        ??Exception?dispatchException?=?null;

        ??try?{
        ???//?校驗(yàn)是否是文件上傳請(qǐng)求
        ???processedRequest?=?checkMultipart(request);
        ???multipartRequestParsed?=?(processedRequest?!=?request);

        ???//?Determine?handler?for?the?current?request.
        ???//?為當(dāng)前請(qǐng)求找到一個(gè)合適的處理器(Handler)
        ???//?返回值是一個(gè)HandlerExecutionChain,也就是處理器執(zhí)行鏈
        ???mappedHandler?=?getHandler(processedRequest);
        ???if?(mappedHandler?==?null)?{
        ????noHandlerFound(processedRequest,?response);
        ????return;
        ???}

        ???//?Determine?handler?adapter?for?the?current?request.
        ???//?根據(jù)HandlerExecutionChain攜帶的Handler找到合適的HandlerAdapter
        ???HandlerAdapter?ha?=?getHandlerAdapter(mappedHandler.getHandler());

        ???//?Process?last-modified?header,?if?supported?by?the?handler.
        ???//?處理GET請(qǐng)求的緩存
        ???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;
        ????}
        ???}

        ???//?執(zhí)行攔截器的preHandle方法
        ???if?(!mappedHandler.applyPreHandle(processedRequest,?response))?{
        ????return;
        ???}

        ???//?Actually?invoke?the?handler.
        ???//?利用HandlerAdapter來執(zhí)行Handler里對(duì)應(yīng)的處理方法
        ???mv?=?ha.handle(processedRequest,?response,?mappedHandler.getHandler());

        ???if?(asyncManager.isConcurrentHandlingStarted())?{
        ????return;
        ???}

        ???//?如果沒有設(shè)置視圖,則應(yīng)用默認(rèn)的視圖名
        ???applyDefaultViewName(processedRequest,?mv);
        ???//?執(zhí)行攔截器的postHandle方法
        ???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);
        ??}
        ??//?根據(jù)ModelAndView對(duì)象解析視圖
        ??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())?{
        ???//?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);
        ???}
        ??}
        ?}
        }

        該方法就是SpringMVC處理請(qǐng)求的整體流程,其中涉及到幾個(gè)重要的方法。
        getHandler
        該方法定義如下

        /**
        ?*?Return?the?HandlerExecutionChain?for?this?request.
        ?*?為這個(gè)request返回一個(gè)HandlerExecutionChain
        ?*/
        @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;
        }

        調(diào)試信息如下
        根據(jù)調(diào)試信息可以看出,getHandler方法主要是從ListhandlerMappings集合中遍歷查找一個(gè)合適的處理器(Handler),返回的結(jié)果是一個(gè)HandlerExecutionChain。然后再根據(jù)HandlerExecutionChain里攜帶的Handler去獲取HandlerAdapter。
        getHandlerAdapter
        getHandlerAdapter方法定義如下

        /**
        ??*?Return?the?HandlerAdapter?for?this?handler?object.
        ??*?@param?handler?the?handler?object?to?find?an?adapter?for
        ??*?@throws?ServletException?if?no?HandlerAdapter?can?be?found?for?the?handler.?This?is?a?fatal?error.
        ??*/
        ?protected?HandlerAdapter?getHandlerAdapter(Object?handler)?throws?ServletException?{
        ??if?(this.handlerAdapters?!=?null)?{
        ???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");
        ?}

        調(diào)試信息如下
        同樣getHandlerAdapter方法主要是從ListhandlerAdapters集合中遍歷查找一個(gè)合適的處理器適配器(HandlerAdapter),返回的結(jié)果是一個(gè)HandlerAdapter。
        可以看到此處HandlerAdapter真正的實(shí)現(xiàn)類是RequestMappingHandlerAdapter。
        processDispatchResultprocessDispatchResult方法主要根據(jù)方法執(zhí)行完成后封裝的ModelAndView,轉(zhuǎn)發(fā)到對(duì)應(yīng)頁面,定義如下

        /**
        ?*?Handle?the?result?of?handler?selection?and?handler?invocation,?which?is
        ?*?either?a?ModelAndView?or?an?Exception?to?be?resolved?to?a?ModelAndView.
        ?*/
        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);
        ??}
        ?}

        ?//?Did?the?handler?return?a?view?to?render?
        ?if?(mv?!=?null?&&?!mv.wasCleared())?{
        ??//?主要調(diào)用該方法渲染視圖
        ??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())?{
        ??//?Concurrent?handling?started?during?a?forward
        ??return;
        ?}

        ?if?(mappedHandler?!=?null)?{
        ??//?Exception?(if?any)?is?already?handled..
        ??mappedHandler.triggerAfterCompletion(request,?response,?null);
        ?}
        }

        render
        render方法定義如下

        /**
        ?*?Render?the?given?ModelAndView.
        ?*?

        This?is?the?last?stage?in?handling?a?request.?It?may?involve?resolving?the?view?by?name.
        ?*?@param?mv?the?ModelAndView?to?render
        ?*?@param?request?current?HTTP?servlet?request
        ?*?@param?response?current?HTTP?servlet?response
        ?*?@throws?ServletException?if?view?is?missing?or?cannot?be?resolved
        ?*?@throws?Exception?if?there's?a?problem?rendering?the?view
        ?*/
        protected?void?render(ModelAndView?mv,?HttpServletRequest?request,?HttpServletResponse?response)?throws?Exception?{
        ?//?Determine?locale?for?request?and?apply?it?to?the?response.
        ?Locale?locale?=
        ???(this.localeResolver?!=?null???this.localeResolver.resolveLocale(request)?:?request.getLocale());
        ?response.setLocale(locale);

        ?View?view;
        ?String?viewName?=?mv.getViewName();
        ?if?(viewName?!=?null)?{
        ??//?We?need?to?resolve?the?view?name.
        ??//?根據(jù)給定的視圖名稱,解析獲取View對(duì)象
        ??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?{
        ??//?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()?+?"'");
        ??}
        ?}

        ?//?Delegate?to?the?View?object?for?rendering.
        ?if?(logger.isTraceEnabled())?{
        ??logger.trace("Rendering?view?["?+?view?+?"]?");
        ?}
        ?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;
        ?}
        }

        resolveViewName
        resolveViewName方法定義如下

        @Nullable
        protected?View?resolveViewName(String?viewName,?@Nullable?Map?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;
        }

        調(diào)試信息如下
        根據(jù)調(diào)試信息可以看到真正解析視圖的ViewResolver的是InternalResourceViewResolver類,也就是我們經(jīng)常配置的一項(xiàng)類型


        "org.springframework.web.servlet.view.InternalResourceViewResolver">
        ?"prefix"?value="/WEB-INF/views/"?/>
        ?"suffix"?value=".html"?/>

        至此我們就得到了SpringMVC處理請(qǐng)求的完整邏輯

        SpringMVC處理請(qǐng)求的整個(gè)流程已經(jīng)梳理清楚了。
        但是,有兩個(gè)重要的問題沒有解決,那就是:參數(shù)綁定和返回值處理。
        因?yàn)樵诰帉慍ontroller里面的方法的時(shí)候,各種類型的參數(shù)都有,SpringMVC是怎么處理不同類型的參數(shù)的呢?SpringMVC處理請(qǐng)求完成后,一定會(huì)返回ModelAndView嗎,如果加了@ResponseBody注解呢?

        參數(shù)綁定

        在整個(gè)流程中,還有一個(gè)最重要的方法,那就是真正執(zhí)行handler的方法,參數(shù)的綁定和返回值的處理都在這個(gè)方法里,也就是

        //?Actually?invoke?the?handler.
        mv?=?ha.handle(processedRequest,?response,?mappedHandler.getHandler());

        handle
        handle方法的作用是根據(jù)請(qǐng)求參數(shù),執(zhí)行真正的處理方法,并且返回合適的ModelAndView對(duì)象,也有可能返回null。該方法定義如下 在AbstractHandlerMethodAdapter類中

        /**
        ?*?This?implementation?expects?the?handler?to?be?an?{@link?HandlerMethod}.
        ?*/
        @Override
        @Nullable
        public?final?ModelAndView?handle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler)
        ??throws?Exception?{

        ?return?handleInternal(request,?response,?(HandlerMethod)?handler);
        }

        可以看到這個(gè)方法實(shí)現(xiàn)只有一行代碼
        handleInternal
        繼續(xù)深入handleInternal方法

        @Override
        protected?ModelAndView?handleInternal(HttpServletRequest?request,
        ??HttpServletResponse?response,?HandlerMethod?handlerMethod)?throws?Exception?{

        ?ModelAndView?mav;
        ?//?校驗(yàn)指定的請(qǐng)求以獲取受支持的方法類型(GET、POST等)和所需的session
        ?checkRequest(request);

        ?//?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)?{
        ????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...
        ??//?真正執(zhí)行handler的方法
        ??mav?=?invokeHandlerMethod(request,?response,?handlerMethod);
        ?}

        ?if?(!response.containsHeader(HEADER_CACHE_CONTROL))?{
        ??if?(getSessionAttributesHandler(handlerMethod).hasSessionAttributes())?{
        ???applyCacheSeconds(response,?this.cacheSecondsForSessionAttributeHandlers);
        ??}
        ??else?{
        ???prepareResponse(response);
        ??}
        ?}

        ?return?mav;
        }

        invokeHandlerMethod
        繼續(xù)深入invokeHandlerMethod方法

        /**
        ?*?Invoke?the?{@link?RequestMapping}?handler?method?preparing?a?{@link?ModelAndView}
        ?*?if?view?resolution?is?required.
        ?*?執(zhí)行@RequestMapping標(biāo)注的handler方法,如果需要解析視圖就準(zhǔn)備一個(gè)ModelAndView
        ?*/
        @Nullable
        protected?ModelAndView?invokeHandlerMethod(HttpServletRequest?request,
        ??HttpServletResponse?response,?HandlerMethod?handlerMethod)?throws?Exception?{

        ?ServletWebRequest?webRequest?=?new?ServletWebRequest(request,?response);
        ?try?{
        ??WebDataBinderFactory?binderFactory?=?getDataBinderFactory(handlerMethod);
        ??ModelFactory?modelFactory?=?getModelFactory(handlerMethod,?binderFactory);

        ??// HandlerMethod接口封裝執(zhí)行方法的信息,提供對(duì)方法參數(shù),方法返回值,方法注釋等的便捷訪問。
        ??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可以看做ModelAndView的上下文容器,關(guān)聯(lián)著Model和View的信息
        ??ModelAndViewContainer?mavContainer?=?new?ModelAndViewContainer();
        ??mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
        ??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);

        ??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);
        ??}

        ??//?真正執(zhí)行Handler的方法
        ??invocableMethod.invokeAndHandle(webRequest,?mavContainer);
        ??if?(asyncManager.isConcurrentHandlingStarted())?{
        ???return?null;
        ??}

        ??//?獲取ModelAndeView對(duì)象
        ??return?getModelAndView(mavContainer,?modelFactory,?webRequest);
        ?}
        ?finally?{
        ??webRequest.requestCompleted();
        ?}
        }

        invokeAndHandle
        invokeAndHandle方法的作用是執(zhí)行并處理真正響應(yīng)請(qǐng)求的方法,該方法定義如下

        /**
        ?*?Invoke?the?method?and?handle?the?return?value?through?one?of?the
        ?*?configured?{@link?HandlerMethodReturnValueHandler?HandlerMethodReturnValueHandlers}.
        ?*?@param?webRequest?the?current?request
        ?*?@param?mavContainer?the?ModelAndViewContainer?for?this?request
        ?*?@param?providedArgs?"given"?arguments?matched?by?type?(not?resolved)
        ?*/
        public?void?invokeAndHandle(ServletWebRequest?webRequest,?ModelAndViewContainer?mavContainer,
        ??Object...?providedArgs)?throws?Exception?{

        ?//?執(zhí)行handler的方法
        ?Object?returnValue?=?invokeForRequest(webRequest,?mavContainer,?providedArgs);
        ?setResponseStatus(webRequest);

        ?if?(returnValue?==?null)?{
        ??if?(isRequestNotModified(webRequest)?||?getResponseStatus()?!=?null?||?mavContainer.isRequestHandled())?{
        ???disableContentCachingIfNecessary(webRequest);
        ???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)?{
        ??if?(logger.isTraceEnabled())?{
        ???logger.trace(formatErrorForReturnValue(returnValue),?ex);
        ??}
        ??throw?ex;
        ?}
        }

        invokeForRequest

        /**
        ?*?Invoke?the?method?after?resolving?its?argument?values?in?the?context?of?the?given?request.
        ?*?

        Argument?values?are?commonly?resolved?through
        ?*?{@link?HandlerMethodArgumentResolver?HandlerMethodArgumentResolvers}.
        ?*?The?{@code?providedArgs}?parameter?however?may?supply?argument?values?to?be?used?directly,
        ?*?i.e.?without?argument?resolution.?Examples?of?provided?argument?values?include?a
        ?*?{@link?WebDataBinder},?a?{@link?SessionStatus},?or?a?thrown?exception?instance.
        ?*?Provided?argument?values?are?checked?before?argument?resolvers.
        ?*?

        Delegates?to?{@link?#getMethodArgumentValues}?and?calls?{@link?#doInvoke}?with?the
        ?*?resolved?arguments.
        ?*?@param?request?the?current?request
        ?*?@param?mavContainer?the?ModelAndViewContainer?for?this?request
        ?*?@param?providedArgs?"given"?arguments?matched?by?type,?not?resolved
        ?*?@return?the?raw?value?returned?by?the?invoked?method
        ?*?@throws?Exception?raised?if?no?suitable?argument?resolver?can?be?found,
        ?*?or?if?the?method?raised?an?exception
        ?*?@see?#getMethodArgumentValues
        ?*?@see?#doInvoke
        ?*/
        @Nullable
        public?Object?invokeForRequest(NativeWebRequest?request,?@Nullable?ModelAndViewContainer?mavContainer,
        ??Object...?providedArgs)?throws?Exception?{

        ?//?獲取參數(shù)
        ?Object[]?args?=?getMethodArgumentValues(request,?mavContainer,?providedArgs);
        ?if?(logger.isTraceEnabled())?{
        ??logger.trace("Arguments:?"?+?Arrays.toString(args));
        ?}
        ?//?執(zhí)行
        ?return?doInvoke(args);
        }

        真正的執(zhí)行無非就是通過反射invoke,所以更重要的是參數(shù)是如何綁定的,詳情就在getMethodArgumentValues方法
        getMethodArgumentValues
        getMethodArgumentValues方法用于從request請(qǐng)求中獲取真正的參數(shù),返回的是Object數(shù)組,該方法定義如下

        /**
        ?*?Get?the?method?argument?values?for?the?current?request,?checking?the?provided
        ?*?argument?values?and?falling?back?to?the?configured?argument?resolvers.
        ?*?

        The?resulting?array?will?be?passed?into?{@link?#doInvoke}.
        ?*?@since?5.1.2
        ?*/
        protected?Object[]?getMethodArgumentValues(NativeWebRequest?request,?@Nullable?ModelAndViewContainer?mavContainer,
        ??Object...?providedArgs)?throws?Exception?{

        ?//?獲取方法上所有的參數(shù)
        ?MethodParameter[]?parameters?=?getMethodParameters();
        ?if?(ObjectUtils.isEmpty(parameters))?{
        ??return?EMPTY_ARGS;
        ?}

        ?Object[]?args?=?new?Object[parameters.length];
        ?for?(int?i?=?0;?i???MethodParameter?parameter?=?parameters[i];
        ??parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        ??args[i]?=?findProvidedArgument(parameter,?providedArgs);
        ??if?(args[i]?!=?null)?{
        ???continue;
        ??}
        ??if?(!this.resolvers.supportsParameter(parameter))?{
        ???throw?new?IllegalStateException(formatArgumentError(parameter,?"No?suitable?resolver"));
        ??}
        ??try?{
        ???
        ???args[i]?=?this.resolvers.resolveArgument(parameter,?mavContainer,?request,?this.dataBinderFactory);
        ??}
        ??catch?(Exception?ex)?{
        ???//?Leave?stack?trace?for?later,?exception?may?actually?be?resolved?and?handled...
        ???if?(logger.isDebugEnabled())?{
        ????String?exMsg?=?ex.getMessage();
        ????if?(exMsg?!=?null?&&?!exMsg.contains(parameter.getExecutable().toGenericString()))?{
        ?????logger.debug(formatArgumentError(parameter,?exMsg));
        ????}
        ???}
        ???throw?ex;
        ??}
        ?}
        ?return?args;
        }

        根據(jù)調(diào)試信息可以看到,用來處理請(qǐng)求參數(shù)的類是HandlerMethodArgumentResolver接口的實(shí)現(xiàn)類HandlerMethodArgumentResolverComposite,此時(shí)正在處理的參數(shù)是一個(gè)Student對(duì)象,并且已經(jīng)把值注綁定了,也就是說真正執(zhí)行綁定的是方法resolveArgumentresolveArgument
        resolveArgument是真正執(zhí)行綁定的的方法

        根據(jù)調(diào)試信息可以看到,用來處理請(qǐng)求參數(shù)的類是HandlerMethodArgumentResolver接口的實(shí)現(xiàn)類HandlerMethodArgumentResolverComposite,此時(shí)正在處理的參數(shù)是一個(gè)Student對(duì)象,并且已經(jīng)把值注綁定了,也就是說真正執(zhí)行綁定的是方法resolveArgument

        resolveArgument
        resolveArgument是真正執(zhí)行綁定的的方法

        getArgumentResolvergetArgumentResolver該方法用于執(zhí)行參數(shù)的綁定,定義如下

        /**
        ?*?Find?a?registered?{@link?HandlerMethodArgumentResolver}?that?supports
        ?*?the?given?method?parameter.
        ?*/
        @Nullable
        private?HandlerMethodArgumentResolver?getArgumentResolver(MethodParameter?parameter)?{
        ?HandlerMethodArgumentResolver?result?=?this.argumentResolverCache.get(parameter);
        ?if?(result?==?null)?{
        ??for?(HandlerMethodArgumentResolver?resolver?:?this.argumentResolvers)?{
        ???if?(resolver.supportsParameter(parameter))?{
        ????result?=?resolver;
        ????this.argumentResolverCache.put(parameter,?result);
        ????break;
        ???}
        ??}
        ?}
        ?return?result;
        }

        該方法的邏輯就是先從argumentResolver緩存中找到能夠執(zhí)行參數(shù)綁定的HandlerMethodArgumentResolver,如果找不到就從HandlerMethodArgumentResolver找,SpringMVC支持的HandlerMethodArgumentResolver一共有26種,用來解析各種類型的參數(shù)

        根據(jù)博主的調(diào)試可以知道
        RequestParamMethodArgumentResolver:處理普通參數(shù)(基本類型、包裝類型、String),不管加不加@RequestParam注解 ServletModelAttributeMethodProcessor:處理POJO類型的參數(shù),比如自定義的Student對(duì)象 RequestResponseBodyMethodProcessor:處理@RequestBody注解類型的參數(shù) 有興趣的同學(xué)可以試試更多不同形式的參數(shù)
        resolveArgument
        由于不同類型的參數(shù)有不同的HandlerMethodArgumentResolver來處理,此處選取POJO類型參數(shù)的注入實(shí)現(xiàn),對(duì)應(yīng)的參數(shù)解析類是ModelAttributeMethodProcessor,其中resolveArgument方法用來解析(綁定)參數(shù)方法定義如下

        /**
        ?*?Resolve?the?argument?from?the?model?or?if?not?found?instantiate?it?with
        ?*?its?default?if?it?is?available.?The?model?attribute?is?then?populated
        ?*?with?request?values?via?data?binding?and?optionally?validated
        ?*?if?{@[email protected]}?is?present?on?the?argument.
        ?*?@throws?BindException?if?data?binding?and?validation?result?in?an?error
        ?*?and?the?next?method?parameter?is?not?of?type?{@link?Errors}
        ?*?@throws?Exception?if?WebDataBinder?initialization?fails
        ?*/
        @Override
        @Nullable
        public?final?Object?resolveArgument(MethodParameter?parameter,?@Nullable?ModelAndViewContainer?mavContainer,
        ??NativeWebRequest?webRequest,?@Nullable?WebDataBinderFactory?binderFactory)?throws?Exception?{

        ?Assert.state(mavContainer?!=?null,?"ModelAttributeMethodProcessor?requires?ModelAndViewContainer");
        ?Assert.state(binderFactory?!=?null,?"ModelAttributeMethodProcessor?requires?WebDataBinderFactory");

        ?//?獲取參數(shù)名
        ?String?name?=?ModelFactory.getNameForParameter(parameter);
        ?//?獲取參數(shù)上的ModelAttribute注解
        ?ModelAttribute?ann?=?parameter.getParameterAnnotation(ModelAttribute.class);
        ?if?(ann?!=?null)?{
        ??mavContainer.setBinding(name,?ann.binding());
        ?}

        ?Object?attribute?=?null;
        ?BindingResult?bindingResult?=?null;

        ?if?(mavContainer.containsAttribute(name))?{
        ??attribute?=?mavContainer.getModel().get(name);
        ?}
        ?else?{
        ??//?Create?attribute?instance
        ??try?{
        ???//?創(chuàng)建參數(shù)類型的實(shí)例(未注入值),底層就是通過反射調(diào)用構(gòu)造方法
        ???attribute?=?createAttribute(name,?parameter,?binderFactory,?webRequest);
        ??}
        ??catch?(BindException?ex)?{
        ???if?(isBindExceptionRequired(parameter))?{
        ????//?No?BindingResult?parameter?->?fail?with?BindException
        ????throw?ex;
        ???}
        ???//?Otherwise,?expose?null/empty?value?and?associated?BindingResult
        ???if?(parameter.getParameterType()?==?Optional.class)?{
        ????attribute?=?Optional.empty();
        ???}
        ???bindingResult?=?ex.getBindingResult();
        ??}
        ?}

        ?if?(bindingResult?==?null)?{
        ??//?Bean?property?binding?and?validation;
        ??//?skipped?in?case?of?binding?failure?on?construction.
        ??WebDataBinder?binder?=?binderFactory.createBinder(webRequest,?attribute,?name);
        ??if?(binder.getTarget()?!=?null)?{
        ???if?(!mavContainer.isBindingDisabled(name))?{
        ????//?真正執(zhí)行綁定(值注入)的方法
        ????bindRequestParameters(binder,?webRequest);
        ???}
        ???validateIfApplicable(binder,?parameter);
        ???if?(binder.getBindingResult().hasErrors()?&&?isBindExceptionRequired(binder,?parameter))?{
        ????throw?new?BindException(binder.getBindingResult());
        ???}
        ??}
        ??//?Value?type?adaptation,?also?covering?java.util.Optional
        ??if?(!parameter.getParameterType().isInstance(attribute))?{
        ???attribute?=?binder.convertIfNecessary(binder.getTarget(),?parameter.getParameterType(),?parameter);
        ??}
        ??bindingResult?=?binder.getBindingResult();
        ?}

        ?//?Add?resolved?attribute?and?BindingResult?at?the?end?of?the?model
        ?Map?bindingResultModel?=?bindingResult.getModel();
        ?mavContainer.removeAttributes(bindingResultModel);
        ?mavContainer.addAllAttributes(bindingResultModel);

        ?return?attribute;
        }

        根據(jù)調(diào)試信息也可以看到bindRequestParameters(binder, webRequest)執(zhí)行完成之后,POJO類型的參數(shù)已經(jīng)完成了綁定。
        bindRequestParameters

        /**
        ?*?This?implementation?downcasts?{@link?WebDataBinder}?to
        ?*?{@link?ServletRequestDataBinder}?before?binding.
        ?*?@see?ServletRequestDataBinderFactory
        ?*/
        @Override
        protected?void?bindRequestParameters(WebDataBinder?binder,?NativeWebRequest?request)?{
        ?ServletRequest?servletRequest?=?request.getNativeRequest(ServletRequest.class);
        ?Assert.state(servletRequest?!=?null,?"No?ServletRequest");
        ?ServletRequestDataBinder?servletBinder?=?(ServletRequestDataBinder)?binder;
        ?//?執(zhí)行綁定的方法
        ?servletBinder.bind(servletRequest);
        }

        bind
        繼續(xù)深入bind方法

        public?void?bind(ServletRequest?request)?{
        ?//?獲取所有參數(shù)的鍵值對(duì)
        ?MutablePropertyValues?mpvs?=?new?ServletRequestParameterPropertyValues(request);
        ?//?處理文件上傳請(qǐng)求
        ?MultipartRequest?multipartRequest?=?WebUtils.getNativeRequest(request,?MultipartRequest.class);
        ?if?(multipartRequest?!=?null)?{
        ??bindMultipart(multipartRequest.getMultiFileMap(),?mpvs);
        ?}
        ?//?把url中攜帶的參數(shù)也加入到MutablePropertyValues
        ?addBindValues(mpvs,?request);
        ?//?執(zhí)行綁定(注入值)
        ?doBind(mpvs);
        }

        由于調(diào)用層次過深,所以無法一步步列出下面的步驟,doBind方法的原理還是通過調(diào)用POJO對(duì)象里的setter方法設(shè)置值,可以查看最終的調(diào)試信息
        根據(jù)調(diào)試信息可以看到,最終執(zhí)行的還是POJO對(duì)象的setter方法,具體執(zhí)行的類是BeanWrapperImpl。
        了解了參數(shù)的綁定,再來看返回值的處理。

        返回值處理

        invokeAndHandle 回到源碼invokeAndHandle方法處(ServletInvocableHandlerMethod類中),該方法定義如下

        /**
        ?*?Invoke?the?method?and?handle?the?return?value?through?one?of?the
        ?*?configured?{@link?HandlerMethodReturnValueHandler?HandlerMethodReturnValueHandlers}.
        ?*?@param?webRequest?the?current?request
        ?*?@param?mavContainer?the?ModelAndViewContainer?for?this?request
        ?*?@param?providedArgs?"given"?arguments?matched?by?type?(not?resolved)
        ?*/
        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())?{
        ???disableContentCachingIfNecessary(webRequest);
        ???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)?{
        ??if?(logger.isTraceEnabled())?{
        ???logger.trace(formatErrorForReturnValue(returnValue),?ex);
        ??}
        ??throw?ex;
        ?}
        }

        真正處理不同類型的返回值的方法是handleReturnValue方法
        handleReturnValue

        /**
        ?*?Iterate?over?registered?{@link?HandlerMethodReturnValueHandler?HandlerMethodReturnValueHandlers}?and?invoke?the?one?that?supports?it.
        ?*?@throws?IllegalStateException?if?no?suitable?{@link?HandlerMethodReturnValueHandler}?is?found.
        ?*/
        @Override
        public?void?handleReturnValue(@Nullable?Object?returnValue,?MethodParameter?returnType,
        ??ModelAndViewContainer?mavContainer,?NativeWebRequest?webRequest)?throws?Exception?{

        ?//?根據(jù)返回值個(gè)返回值類型選取合適的HandlerMethodReturnValueHandler
        ?HandlerMethodReturnValueHandler?handler?=?selectHandler(returnValue,?returnType);
        ?if?(handler?==?null)?{
        ??throw?new?IllegalArgumentException("Unknown?return?value?type:?"?+?returnType.getParameterType().getName());
        ?}
        ?//?真正的處理返回值
        ?handler.handleReturnValue(returnValue,?returnType,?mavContainer,?webRequest);
        }

        selectHandler

        @Nullable
        private?HandlerMethodReturnValueHandler?selectHandler(@Nullable?Object?value,?MethodParameter?returnType)?{
        ?boolean?isAsyncValue?=?isAsyncReturnValue(value,?returnType);
        ?for?(HandlerMethodReturnValueHandler?handler?:?this.returnValueHandlers)?{
        ??if?(isAsyncValue?&&?!(handler?instanceof?AsyncHandlerMethodReturnValueHandler))?{
        ???continue;
        ??}
        ??if?(handler.supportsReturnType(returnType))?{
        ???return?handler;
        ??}
        ?}
        ?return?null;
        }

        根據(jù)調(diào)試信息可以看到,SpringMVC為返回值提供了15個(gè)HandlerMethodReturnValueHandler的實(shí)現(xiàn)了來處理不同類型的返回值。
        事實(shí)上,用來處理@ResponseBody類型的是RequestResponseBodyMethodProcessor。
        如果對(duì)前文參數(shù)綁定還有印象的話,會(huì)發(fā)現(xiàn)@RequestBody類型參數(shù)綁定也是用的這個(gè)類。
        繼續(xù)跟進(jìn)RequestResponseBodyMethodProcessor類的handleReturnValue方法handleReturnValueRequestResponseBodyMethodProcessor類的handleReturnValue方法定義如下
        這里設(shè)置了一個(gè)非常重要的屬性requestHandled,這個(gè)屬性關(guān)系到是否需要返回ModelAndView對(duì)象

        @Override
        public?void?handleReturnValue(@Nullable?Object?returnValue,?MethodParameter?returnType,
        ??ModelAndViewContainer?mavContainer,?NativeWebRequest?webRequest)
        ??throws?IOException,?HttpMediaTypeNotAcceptableException,?HttpMessageNotWritableException?{

        ?//?設(shè)置該請(qǐng)求是否已在處理程序中完全處理,例如@ResponseBody方法不需要視圖解析器,此處就可以設(shè)置為true。
        ?//?當(dāng)控制器方法聲明類型為ServletResponse或OutputStream的參數(shù)時(shí),也可以設(shè)置此標(biāo)志為true。?
        ?//?這個(gè)屬性設(shè)置成true之后,上層getModelAndView獲取ModelAndView時(shí)會(huì)返回Null,因?yàn)椴恍枰晥D。
        ?//?默認(rèn)值為false
        ?mavContainer.setRequestHandled(true);
        ?ServletServerHttpRequest?inputMessage?=?createInputMessage(webRequest);
        ?ServletServerHttpResponse?outputMessage?=?createOutputMessage(webRequest);

        ?//?Try?even?with?null?return?value.?ResponseBodyAdvice?could?get?involved.
        ?//?底層就是利用java.io.OutputStreamWriter類把返回值寫到網(wǎng)絡(luò)IO
        ?writeWithMessageConverters(returnValue,?returnType,?inputMessage,?outputMessage);
        }

        繼續(xù)深入writeWithMessageConverters方法,一步步調(diào)試到最后,底層就是利用java.io.OutputStreamWriter類把返回值寫到網(wǎng)絡(luò)IO

        由于handleReturnValue把requestHandled設(shè)置成了true,上層在調(diào)用getModelAndView方法時(shí)會(huì)返回null,表示該請(qǐng)求不需要視圖。感興趣的同學(xué)自己調(diào)試一下便知。

        總結(jié)

        本文主要從源碼的閱讀和調(diào)試的角度,整體的講解了SpringMVC處理請(qǐng)求的整個(gè)流程,并且講解了參數(shù)的綁定以及返回值的處理。相信大家看完后,結(jié)合自己的調(diào)試信息,會(huì)對(duì)SpringMVC的請(qǐng)求處理過程有一個(gè)更深入的理解。

        推薦閱讀:

        整理了15個(gè)好用的API接口管理免費(fèi)神器,隨便挑...

        Elasticsearch 在互聯(lián)網(wǎng)公司大量真實(shí)的應(yīng)用案例

        5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機(jī),樹莓派,等等。在公眾號(hào)內(nèi)回復(fù)「2048」,即可免費(fèi)獲取??!

        微信掃描二維碼,關(guān)注我的公眾號(hào)

        寫留言

        朕已閱?

        瀏覽 38
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            性一交一乱一乱一按一摩 | 国产老头老太性视频 | 天天看片天天爽 | 一边吃奶一边添p好爽视频观看 | 欧美色爱综合网 | 国内伊人| 成人片黄网站色大片免费视频 | 年轻bbwbbw高潮 | 男人捅女人捅到爽 | 一级a性色生活片毛片 |