<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring MVC 處理一個(gè)請(qǐng)求的流程分析

          共 5854字,需瀏覽 12分鐘

           ·

          2021-02-09 23:52

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          ? 作者?|? 程序員自由之路

          來(lái)源 |? urlify.cn/eYNvY3

          76套java從入門(mén)到精通實(shí)戰(zhàn)課程分享

          Spring MVC是Spring系列框架中使用頻率最高的部分。不管是Spring Boot還是傳統(tǒng)的Spring項(xiàng)目,只要是Web項(xiàng)目都會(huì)使用到Spring MVC部分。因此程序員一定要熟練掌握MVC部分。本篇博客簡(jiǎn)要分析Spring MVC處理一個(gè)請(qǐng)求的流程。

          一個(gè)請(qǐng)求從客戶端發(fā)出到達(dá)服務(wù)器,然后被處理的整個(gè)過(guò)程其實(shí)是非常復(fù)雜的。本博客主要介紹請(qǐng)求到達(dá)服務(wù)器被核心組件DispatcherServlet處理的整理流程(不包括Filter的處理流程)。

          1. 處理流程分析

          Servlet處理一個(gè)請(qǐng)求時(shí)會(huì)調(diào)用service()方法,所以DispatcherServlet處理請(qǐng)求的方式也是從service()方法開(kāi)始(debug的話建議從DispatcherServlet的service方法開(kāi)始debug)。FrameworkServlet重寫(xiě)了HttpServlet的service方法,這個(gè)service方法后面又調(diào)用了FrameworkServlet的processRequest()方法,processRequest()調(diào)用了DispatcherServlet的doService()方法,最后調(diào)用到DispatcherServlet的doDispatcher()方法。整合處理請(qǐng)求的方法調(diào)用流程如上,下面看下代碼:

          protected?void?service(HttpServletRequest?request,?HttpServletResponse?response)
          ??throws?ServletException,?IOException?{

          ?HttpMethod?httpMethod?=?HttpMethod.resolve(request.getMethod());
          ?if?(HttpMethod.PATCH?==?httpMethod?||?httpMethod?==?null)?{
          ??processRequest(request,?response);
          ?}
          ?else?{
          ????????//這邊調(diào)用了HttpServlet的service()方法,但由于FrameWorkServle重寫(xiě)了doGet、doPost等方法,所以最終還是會(huì)調(diào)用到processRequest方法
          ??super.service(request,?response);
          ?}
          }

          再看看FrameworkServlet的processRequest()方法。

          ???protected?final?void?processRequest(HttpServletRequest?request,?HttpServletResponse?response)
          ??????throws?ServletException,?IOException?{
          ????
          ?????long?startTime?=?System.currentTimeMillis();
          ?????Throwable?failureCause?=?null;
          ????
          ?????LocaleContext?previousLocaleContext?=?LocaleContextHolder.getLocaleContext();
          ?????LocaleContext?localeContext?=?buildLocaleContext(request);
          ????
          ?????RequestAttributes?previousAttributes?=?RequestContextHolder.getRequestAttributes();
          ?????ServletRequestAttributes?requestAttributes?=?buildRequestAttributes(request,?response,?previousAttributes);
          ????
          ?????WebAsyncManager?asyncManager?=?WebAsyncUtils.getAsyncManager(request);
          ?????asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(),?new?RequestBindingInterceptor());
          ????
          ?????initContextHolders(request,?localeContext,?requestAttributes);
          ????
          ?????try?{
          ????????????//這邊調(diào)用DispatcherServlet的doService()方法
          ??????doService(request,?response);
          ?????}
          ?????catch?(ServletException?ex)?{
          ??????failureCause?=?ex;
          ??????throw?ex;
          ?????}
          ?????catch?(IOException?ex)?{
          ??????failureCause?=?ex;
          ??????throw?ex;
          ?????}
          ?????catch?(Throwable?ex)?{
          ??????failureCause?=?ex;
          ??????throw?new?NestedServletException("Request?processing?failed",?ex);
          ?????}
          ????
          ?????finally?{
          ??????resetContextHolders(request,?previousLocaleContext,?previousAttributes);
          ??????if?(requestAttributes?!=?null)?{
          ???????requestAttributes.requestCompleted();
          ??????}
          ????
          ??????if?(logger.isDebugEnabled())?{
          ???????if?(failureCause?!=?null)?{
          ????????this.logger.debug("Could?not?complete?request",?failureCause);
          ???????}
          ???????else?{
          ????????if?(asyncManager.isConcurrentHandlingStarted())?{
          ?????????logger.debug("Leaving?response?open?for?concurrent?processing");
          ????????}
          ????????else?{
          ?????????this.logger.debug("Successfully?completed?request");
          ????????}
          ???????}
          ??????}
          ????
          ??????publishRequestHandledEvent(request,?response,?startTime,?failureCause);
          ?????}
          ????}

          doService()方法的具體內(nèi)容會(huì)在后面講到,這邊描述下doDispatcher()的內(nèi)容,參考了博客:

          首先根據(jù)請(qǐng)求的路徑找到HandlerMethod(帶有Method反射屬性,也就是對(duì)應(yīng)Controller中的方法),然后匹配路徑對(duì)應(yīng)的攔截器,有了HandlerMethod和攔截器構(gòu)造個(gè)HandlerExecutionChain對(duì)象。HandlerExecutionChain對(duì)象的獲取是通過(guò)HandlerMapping接口提供的方法中得到。有了HandlerExecutionChain之后,通過(guò)HandlerAdapter對(duì)象進(jìn)行處理得到ModelAndView對(duì)象,HandlerMethod內(nèi)部handle的時(shí)候,使用各種HandlerMethodArgumentResolver實(shí)現(xiàn)類處理HandlerMethod的參數(shù),使用各種HandlerMethodReturnValueHandler實(shí)現(xiàn)類處理返回值。最終返回值被處理成ModelAndView對(duì)象,這期間發(fā)生的異常會(huì)被HandlerExceptionResolver接口實(shí)現(xiàn)類進(jìn)行處理。

          總結(jié)下Spring MVC處理一個(gè)請(qǐng)求的過(guò)程:

          • 首先,搜索應(yīng)用的上下文對(duì)象 WebApplicationContext 并把它作為一個(gè)屬性(attribute)綁定到該請(qǐng)求上,以便控制器和其他組件能夠使用它。

          • 將地區(qū)(locale)解析器綁定到請(qǐng)求上,以便其他組件在處理請(qǐng)求(渲染視圖、準(zhǔn)備數(shù)據(jù)等)時(shí)可以獲取區(qū)域相關(guān)的信息。如果你的應(yīng)用不需要解析區(qū)域相關(guān)的信息;

          • 將主題(theme)解析器綁定到請(qǐng)求上,以便其他組件(比如視圖等)能夠了解要渲染哪個(gè)主題文件。同樣,如果你不需要使用主題相關(guān)的特性,忽略它即可如果你配置了multipart文件處理器,那么框架將查找該文件是不是multipart(分為多個(gè)部分連續(xù)上傳)的。若是,則將該請(qǐng)求包裝成一個(gè) MultipartHttpServletRequest 對(duì)象,以便處理鏈中的其他組件對(duì)它做進(jìn)一步的處理。關(guān)于Spring對(duì)multipart文件傳輸處理的支持;

          • 為該請(qǐng)求查找一個(gè)合適的處理器。如果可以找到對(duì)應(yīng)的處理器,則與該處理器關(guān)聯(lián)的整條執(zhí)行鏈(前處理器、后處理器、控制器等)都會(huì)被執(zhí)行,以完成相應(yīng)模型的準(zhǔn)備或視圖的渲染如果處理器返回的是一個(gè)模型(model),那么框架將渲染相應(yīng)的視圖。若沒(méi)有返回任何模型(可能是因?yàn)榍昂蟮奶幚砥鞒鲇谀承┰驍r截了請(qǐng)求等,比如,安全問(wèn)題),則框架不會(huì)渲染任何視圖,此時(shí)認(rèn)為對(duì)請(qǐng)求的處理可能已經(jīng)由處理鏈完成了(這個(gè)過(guò)程就是doService()和doDispatcher()做的事情)

          1、 首先用戶發(fā)送請(qǐng)求——>DispatcherServlet,前端控制器收到請(qǐng)求后自己不進(jìn)行處理,而是委托給其他的解析器進(jìn)行處理,作為統(tǒng)一訪問(wèn)點(diǎn),進(jìn)行全局的流程控制;

          2、 DispatcherServlet——>HandlerMapping,HandlerMapping將會(huì)把請(qǐng)求映射為HandlerExecutionChain對(duì)象(包含一個(gè)Handler處理器(頁(yè)面控制器)對(duì)象、多個(gè)HandlerInterceptor攔截器)對(duì)象,通過(guò)這種策略模式,很容易添加新的映射策略;

          3、 DispatcherServlet——>HandlerAdapter,HandlerAdapter將會(huì)把處理器包裝為適配器,從而支持多種類型的處理器,即適配器設(shè)計(jì)模式的應(yīng)用,從而很容易支持很多類型的處理器;

          4、 HandlerAdapter——>處理器功能處理方法的調(diào)用,HandlerAdapter將會(huì)根據(jù)適配的結(jié)果調(diào)用真正的處理器的功能處理方法,完成功能處理;并返回一個(gè)ModelAndView對(duì)象(包含模型數(shù)據(jù)、邏輯視圖名);

          5、 ModelAndView的邏輯視圖名——> ViewResolver,ViewResolver將把邏輯視圖名解析為具體的View,通過(guò)這種策略模式,很容易更換其他視圖技術(shù);

          6、 View——>渲染,View會(huì)根據(jù)傳進(jìn)來(lái)的Model模型數(shù)據(jù)進(jìn)行渲染,此處的Model實(shí)際是一個(gè)Map數(shù)據(jù)結(jié)構(gòu),因此很容易支持其他視圖技術(shù);

          7、返回控制權(quán)給DispatcherServlet,由DispatcherServlet返回響應(yīng)給用戶,到此一個(gè)流程結(jié)束。

          2. 請(qǐng)求流程圖

          還是這個(gè)圖比較清楚。發(fā)現(xiàn)根據(jù)代碼不太能把這個(gè)流程說(shuō)清楚。而且整個(gè)流程很長(zhǎng),代碼很多,我就不貼代碼了。這里根據(jù)這個(gè)圖再把整個(gè)流程中組件的功能總結(jié)下:

          • DispatcherServlet:核心控制器,所有請(qǐng)求都會(huì)先進(jìn)入DispatcherServlet進(jìn)行統(tǒng)一分發(fā),是不是感覺(jué)有點(diǎn)像外觀模式的感覺(jué);

          • HandlerMapping:這個(gè)組件的作用就是將用戶請(qǐng)求的URL映射成一個(gè)HandlerExecutionChain。這個(gè)HandlerExecutionChainHandlerMethodHandlerInterceptor的組合。Spring在啟動(dòng)的時(shí)候會(huì)默認(rèn)注入很多HandlerMapping組件,其中最常用的組件就是RequestMappingHandlerMapping。

            上面的HandlerMethodHandlerInterceptor組件分別對(duì)應(yīng)我們Controller中的方法和攔截器。攔截器會(huì)在HandlerMethod方法執(zhí)行之前執(zhí)行

          • HandlerAdapter組件,這個(gè)組件的主要作用是用來(lái)對(duì)HandlerMethod中參數(shù)的轉(zhuǎn)換,對(duì)方法的執(zhí)行,以及對(duì)返回值的轉(zhuǎn)換等等。這里面涉及的細(xì)節(jié)就很多了,包括HandlerMethodArgumentResolverHandlerMethodReturnValueHandler?、RequestResponseBodyMethodProcessor?、和HttpMessageConvert等組件。

            當(dāng)HandlerAdapter組件執(zhí)行完成之后會(huì)得到一個(gè)ModleAndView組件,這個(gè)組件代表視圖模型。

          • 得到ModleAndView后會(huì)執(zhí)行攔截器的postHandle方法。

          • 如果在上面的執(zhí)行過(guò)程中發(fā)生任何異常,會(huì)由HandlerExceptionResolver進(jìn)行統(tǒng)一處理。

          • 最后模型解析器會(huì)對(duì)上面的到的ModleAndView進(jìn)行解析,得到一個(gè)一個(gè)View返回給客戶端。在返回客戶端之前還會(huì)執(zhí)行攔截器的afterCompletion方法。





          粉絲福利:Java從入門(mén)到入土學(xué)習(xí)路線圖

          ??????

          ??長(zhǎng)按上方微信二維碼?2 秒


          感謝點(diǎn)贊支持下哈?

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

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  午夜婷婷| www.天天干 | 国产1234第一页 | 伊人在线大香蕉视频 | 77777亚洲和欧洲视频在线观看 |