歡迎光臨
每天分享高質量文章

從原始碼的角度來看 SpringMVC

SpringMVC核心流程圖

 

簡單總結

首先請求進入DispatcherServlet 由DispatcherServlet 從HandlerMappings中提取對應的Handler

此時只是獲取到了對應的Handle,然後得去尋找對應的配接器,即:HandlerAdapter

拿到對應HandlerAdapter時,這時候開始呼叫對應的Handler處理業務邏輯了(這時候實際上已經執行完了我們的Controller) 執行完成之後傳回一個ModeAndView

這時候交給我們的ViewResolver通過視圖名稱查找出對應的視圖然後傳回

最後 渲染視圖 傳回渲染後的視圖 –>響應請求

SpringMVC 原始碼解析

首先我們查看繼承關係(關鍵查看藍色箭頭路線) 會發現DispatcherServlet無非就是一個HttpServlet

由此,我們可以去查看Servlet的關鍵方法:service,doGet,doPost

  •  service:

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

HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(requestresponse);
}
else {
super.service(requestresponse);
}
}

  •  doGet:

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException 
{

processRequest(request, response);
}

  •  doPost:

@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException 
{

   processRequest(request, response);
}

這裡會發現無論是哪個方法最後都呼叫了 processRequest(request, response);我們把焦點放在這個方法上,會發現一個核心的方法:

doService(request, response);然後會發現 這個方法貌似,呃..有點不一樣:

protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception
;它居然是一個抽象方法...這就得回到剛剛的繼承關係中,找到他的子類了:DispatcherServlet

反正我看到這個方法的實現的時候,腦海裡就浮現出4個字:花 里 胡 哨 。代碼太多,就不放在筆記裡面了,太占地方了.. 為什麼這樣說呢? 因為你看完之後會發現關鍵在於:doDispatch(request, response);是的,沒看錯,這一行才是關鍵!

我們把視角切入這個方法 (至於代碼..還是不放進來了.. ) 總結一下:

把要用的變數都定義好:比如我們的ModelAndView以及異常..等等;

下麵即將看到的是一個熟悉的陌生人(噢不,關鍵詞)

processedRequest = checkMultipart(request);

 “Multipart” 這個關鍵詞好像在哪見過??..讓我想想..(漸漸步入了知識盲區) 哦對!在檔案上傳的時候!(勉強想起來了。。) 是的,其實這行代碼就是判斷當前請求是否是一個二進制請求(有沒有帶檔案) 當然 這裡只是提一下,並不是本文的核心內容。。。(有時間的小伙伴可以自己去瞭解一下)

好的,現在回到我們的主題,來看看這個方法:

mappedHandler = getHandler(processedRequest);

看過我們上面流程圖的同學應該會知道他現在在幹嘛。 現在來獲取我們的Handler了..從哪獲取呢? 從他的HandlerMapping裡面獲取。

問題1:至於這個HandlerMappings 哪裡來的呢

這個等下討論 我們先來看看他獲取的代碼:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
      for (HandlerMapping hm : this.handlerMappings) {
         if (logger.isTraceEnabled()) {
            logger.trace(
                  "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
         }
         HandlerExecutionChain handler = hm.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   return null;
}

我們能看見他是在遍歷一個handlerMappings

問題2:至於這個handlerMapping是什麼呢

是2個我們不認識的東西,至於是什麼,現在說了也不知道,我們繼續往下走,可以看見圖片上1188行代碼, 他從這個mapping 裡面獲取了一個handler 如果獲取到了 這個方法就走完了, 不然就下一次迴圈

問題1解釋:

我們先回到剛剛那個問題,這個HandlerMapping 哪裡來的呢。 不多說,上圖:

我們在原始碼包的DispatcherServlet.properties檔案下會看見, 他定義了圖片里的這些屬性。 重點放在方框內,第一個屬性,就是我們剛看見的HandlerMappings, 也就是說 HandlerMappings也就是他自己事先定義好的呢。至於第二個屬性,咱們待會兒見~

也就是說SpringMVC自己自帶了2個HandlerMapping 來供我們選擇 至於 為什麼要有2個呢? 這時候得啟動專案從斷點的角度來看看了;

我們用2種方式來註冊Controller 分別是:

  • 作為Bean的形式:

@Component("/test")
public class TesrController  implements org.springframework.web.servlet.mvc.Controller{


    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("1");
        return null;
    }
}

  • 以Annotation形式:

@Controller
public class AnnotationController {

    @RequestMapping("/test2")
    public Object test(){

        System.out.println("test");

        return null;
    }
}

我們先用Bean的方式來跑:

視角走到我們的mappedHandler = getHandler(processedRequest);裡面

問題2解釋:

來,跟著箭頭走,我們發現 我們以Bean的形式註冊的Controller 可以從這個BeanNameUrlHandlerMapping裡面獲取到對應的Handler ; 這裡 我們是不是對於這個HandlerMapping有了懵懂的瞭解了?

猜想1:

 我們來猜一下 如果是以Annotation的形式註冊的Controller的話 就會被第二個HandlerMapping獲取到。 至於對不對 這個問題我們先留著。

我們先讓代碼繼續走,會發現 Handler傳回出來緊接著會執行下麵這個方法,這個方法我們從流程圖中可以瞭解到,就是在找一個配接器。

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

問題3:何為配接器?

我們先來看看他這個方法裡面幹了啥:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   if (this.handlerAdapters != null) {
      for (HandlerAdapter ha : this.handlerAdapters) {
         if (logger.isTraceEnabled()) {
            logger.trace("Testing handler adapter [" + ha + "]");
         }
         if (ha.supports(handler)) {
            return ha;
         }
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

其實能看見他是從一個handlerAdapters屬性裡面遍歷了我們的配接器 這個handlerAdapters哪來的呢? 跟我們的HandlerMappings一樣 在他的配置檔案裡面有寫,就是我們剛剛所說的 待會兒見的那個東西~ 不多說 上圖:

問題3解釋:

至於什麼是配接器,我們結合Handler來講, 就如我們在最開始的總結時所說的, 一開始只是找到了Handler 現在要執行了,但是有個問題,Handler不止一個, 自然而然對應的執行方式就不同了, 這時候配接器的概念就出來了:對應不同的Handler的執行方案。

當找到合適的配接器的時候, 基本上就已經收尾了,因為後面在做了一些判斷之後(判斷請求型別之類的),就開始執行了你的Handler了,上代碼:

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

這個mv就是我們的ModlAndView 其實執行完這一行 我們的Controller的邏輯已經執行完了, 剩下的就是尋找視圖 渲染圖的事情了….

我們這裡只是使用了Bean的形式執行了一遍流程 假設使用Annotation呢?

SpringMVC BeanName方式和Annotation方式註冊Controller原始碼分析

現在我們來使用Annotation來註冊Controller看看。我們這裡只看不同的地方。

猜想1證明:

首先在這個HandlerMappings這裡之前的那個就不行了 這裡採用了另外一個HandlerMapping 其實也就證明瞭我們的猜想1

然後就是到了我們的配接器了:

這裡我們會看到用的是這個配接器 而我們的Bean方式註冊的Controller 的話 使用的是另外兩個配接器來的,至於有什麼區別呢? 我們來看看他執行的時候:

@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception 
{

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

我們的Annotation的形式 是拿到這個handler作為一個HandlerMethod 也就是一個方法物件來執行 這時候我們看看Bean是什麼樣子的:

@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception 
{

   return ((Controller) handler).handleRequest(request, response);
}

由最開始可以看到 我們如果以Bean的形式註冊Controller的話 我們的實現一個Controller的接口 在這裡 他把我們的handler強制轉換為一個Controller來執行了。

總結

其實我們的SpringMVC關鍵的概念就在於Handler(處理器) 和Adapter(配接器)

通過一個關鍵的HandlerMappings 找到合適處理你的Controller的Handler 然後再通過HandlerAdapters找到一個合適的HandlerAdapter 來執行Handler即Controller裡面的邏輯。 最後再傳回ModlAndView…

赞(0)

分享創造快樂