從源碼角度瞭解SpringMVC的執行流程

從源碼角度瞭解SpringMVC的執行流程

  SpringMVC的執行流程網上有不少帖子都有講解,流程圖和文字描述都很詳細,可是你若是沒有經過具體源碼本身走一遍流程,其實只是死記硬背。因此想開個帖子從源碼角度再梳理一遍SpringMVC的執行流程,加深印象。java

SpringMVC介紹

  SpringMVC採用的是前端控制器(Front Controller) + 各個業務處理器(Controller)來處理請求的。前端控制器來響應全部請求,經過必定的調度規則找到具體負責處理的業務處理器,並將請求委派給具體的業務處理器去執行業務邏輯,業務處理器返回給前端控制器模型數據model,最後前端控制器將model交給視圖View進行渲染。web

源碼分析思路

  看源碼的同窗可能每每會陷入一個怪圈,剛開始看可能還能看懂,等到一層一層點進去會愈來愈暈,讓本身陷入了太多的細節中,而這些細節其實對主要流程並無多大影響,而後就埋頭研究。以後不得不又從頭開始看,又讓本身陷入了另外一個細節。其實看源碼開始時只是須要看一個大體的框架和思路,瞭解代碼的大體執行流程,千萬不要讓本身陷入到細節的泥潭中。因此本文是經過幾個關鍵的接口做爲切入點來梳理SpringMVC的執行流程,若是咱們把關鍵的接口弄懂了,也就瞭解了SpringMVC的執行流程。因此本文只是去了解接口功能,並不關注到具體的實現邏輯上。當咱們把大致流程瞭解後,以後就只是各個擊破具體的實現類了。以後做者還會經過本身來實現這些接口來處理本身定義的請求,結合具體的例子來理解。spring

閱讀SpringMVC源碼給我最大的感觸有兩點:編程

  • 開放封閉原則,SpringMVC中可擴展性很強,咱們只須要實現具體的接口,而後將接口加入到容器中,就能夠實現咱們的擴展功能,不須要改任何代碼,對擴展開放。並且已有實現類中的關鍵方法都是用final修飾的,對修改關閉。
  • 面向接口編程,全部重要的流程代碼,幾乎都是接口調用,而不是具體到某一個特定的類上面。

源碼解讀

注:源碼版本爲 spring-webmvc-5.2.2.RELEASE.jarjson

幾個關鍵接口和類

HandlerMapping

public interface HandlerMapping {
    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}

HandlerMapping映射了請求與具體處理器的關係,能夠理解爲內存中有這樣一個內存數據 Map<request, Handler>,HandlerMapping就是根據請求從Map中找到Handler返回。HandlerExecutionChain只是將Handler和其對應的攔截器interceptors進行了包裝。mvc

前文所說的調度規則,經過請求的 HttpServletRequest 獲取具體的請求處理類,將請求處理類包裝成 HandlerExecutionChain 返回。其中 HandlerExecutionChain中的Object handler就是具體的請求處理類。app

public class HandlerExecutionChain {
    
    private final Object handler;

    @Nullable
    private HandlerInterceptor[] interceptors;

    @Nullable
    private List<HandlerInterceptor> interceptorList;
}

咱們如今經過請求找到了具體的處理類,那麼咱們怎麼經過處理類去執行具體的方法呢?那麼就須要HandlerAdapter了。框架

HandlerAdapter

public interface HandlerAdapter {
    
    /**
     * 經過方法 supports 判斷適配器是否適配這種類型的Handler,返回true則表明適配。
     */
    boolean supports(Object handler);
    
    /**
     * 若是適配則經過方法 handle 去讓 Object handler 執行具體的處理方法。
     */
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response
                        , Object handler) throws Exception;
    
        long getLastModified(HttpServletRequest request, Object handler);

}

HandlerAdapter 處理器的適配器,幫助前端控制器去執行處理器Handler中具體的處理業務,讓前端控制機不須要關注具體的執行細節,也就是說HandlerAdapter對前端控制機屏蔽了處理器執行的具體細節。jsp

ModelAndView

public class ModelAndView {

    /** View instance or view name String. */
    @Nullable
    private Object view;

    /** Model Map. */
    @Nullable
    private ModelMap model;

對邏輯視圖名view和數據模型的封裝。

Object view爲一般爲String類型的邏輯視圖名。

ModelMap 爲MVC中Model的角色,底層爲Map類型。

ViewResolver

public interface ViewResolver {
    @Nullable
    View resolveViewName(String viewName, Locale locale) throws Exception;

}

解析邏輯視圖名viewName找到具體的View,前端控制器找到具體視圖View的嚮導。

View

public interface View {
        void render(@Nullable Map<String, ?> model, HttpServletRequest request
                    , HttpServletResponse response) throws Exception;

}

調用render方法經過數據模型渲染視圖。

請求參數中的 Map<String, ?> model 在SpringMVC中扮演Model的角色。

好比咱們想返回json格式的數據,那麼render方法邏輯就是將model轉爲json格式輸出。

或者咱們想返回jsp,那麼咱們就能夠model解析到具體的jsp頁面進行展現。

前端控制器 DispatcherServlet

 在SpringMVC中,DispatcherServlet做爲前端控制器,控制服務的具體執行流程,主要的執行流程代碼也都在這個類中。

下面是DispatcherServlet簡化的源碼,只包含了重要的執行流程,保留了關鍵的執行代碼,讀者能夠具體關鍵字進行搜索去找到具體的代碼行。

public class DispatcherServlet extends FrameworkServlet {
    protected void doDispatch(HttpServletRequest request, 
                              HttpServletResponse response) throws Exception {
        HandlerExecutionChain mappedHandler = null;
        ModelAndView mv = null;
        mappedHandler = getHandler(processedRequest);
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
        
        render(mv, request, response);
    }
    
    protected void render(ModelAndView mv, HttpServletRequest request
                          , HttpServletResponse response) throws Exception {
        String viewName = mv.getViewName();
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
        view.render(mv.getModelInternal(), request, response);
    }
代碼流程:
  • 獲取處理器:HandlerMapping經過HttpServletRequest請求找到具體的Handler
  • 獲取處理器對應的適配器:經過Handler找到具體的HandlerAdapter
  • 調用處理器的處理邏輯:HandlerAdapter調用Handler執行具體的處理邏輯,返回ModelAndView
  • 解析視圖:ViewResolver經過ModelAndView中的邏輯視圖名找到具體的View。
  • 渲染視圖:View將數據模型進行渲染。
獲取處理器
@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;
    }

SpringMVC在啓動的時候會掃描全部實現了HandlerMapping接口的類,並將這些類加入到容器中。

獲取處理器其實就是循環實現了HandlerMapping的類,調用getHandler()方法,找到了就中止並返回。

每種類型的Handler都有各自對應的HandlerMapping。好比SpirngMVC中默認的處理器爲 Controller,與之對於的HandlerMapping爲RequestMappingHandlerMapping。

獲取處理器的適配器
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter adapter : this.handlerAdapters) {
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }
}

邏輯和獲取處理器同樣,判斷邏輯就是前面提過的,只要supports方法返回true,則表明適配這個Handler。

同理也是一種Handler應該有與之對應的HandlerAdapter。與Controller對應的爲RequestMappingHandlerAdapter。

因此若是咱們要編寫自定義的處理器。那麼咱們須要本身的Handler類和與之對於的HandlerMapping和HandlerAdapter。

解析視圖
@Nullable
    protected View resolveViewName(String viewName, @Nullable Map<String, Object> 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;
    }

仍然是一樣的邏輯,全部的代碼都是面向接口來開發的。

SpringMVC支持各類各樣的視圖渲染,如JSP、json、freemarker、thymeleaf。ViewResolver就是這些視圖的嚮導,它告訴SpringMVC須要經過什麼方式去找到具體的視圖View。

每種視圖View都有本身對應的視圖解析器,例如FreeMarkerView對應的視圖解析器爲FreeMarkerViewResolver。

視圖渲染

其實就一句代碼。

view.render(mv.getModelInternal(), request, response);

視圖渲染,做者剛開始看到這個詞,以爲好高大上。其實就是讓數據模型model應以什麼樣的方式來展現。

若是你想將model以json格式返回,那麼你就去實現View接口,把model轉爲json格式,而後寫入到響應類的輸出流便可。

ServletOutputStream out = response.getOutputStream();
baos.writeTo(json);
out.flush();

結語

本文只是經過幾個重要的接口來描述SpringMVC的執行流程,沒有具體分析實現類的邏輯。也想在這裏分享下本身看源碼的心得體會。看源碼時千萬不要讓本身陷入過深的業務邏輯中去,先看主要執行流程,重要的接口,好比以debug的方式先預覽下執行的方法棧,根據方法棧去定位。若是有哪些地方有誤或者有不一樣的理解,還請不吝賜教。

相關文章
相關標籤/搜索