SpringMVC工作原理詳解

Java團長_發表於2018-10-30

本文整理自網路,原文出處暫不知,對原文做了較大的改動,在此說明!

先來看一下什麼是 MVC 模式

MVC 是一種設計模式.

MVC 的原理圖如下:


640


SpringMVC 簡單介紹

SpringMVC 框架是以請求為驅動,圍繞 Servlet 設計,將請求發給控制器,然後通過模型物件,分派器來展示請求結果檢視。其中核心類是 DispatcherServlet,它是一個 Servlet,頂層是實現的Servlet介面。

SpringMVC 使用

需要在 web.xml 中配置 DispatcherServlet 。並且需要配置 Spring 監聽器ContextLoaderListener

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <!-- 如果不設定init-param標籤,則必須在/WEB-INF/下建立xxx-servlet.xml檔案,其中xxx是servlet-name中配置的名稱。 -->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/springmvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

SpringMVC 工作原理(重要)

簡單來說:

客戶端傳送請求-> 前端控制器 DispatcherServlet 接受客戶端請求 -> 找到處理器對映 HandlerMapping 解析請求對應的 Handler-> HandlerAdapter 會根據 Handler 來呼叫真正的處理器開處理請求,並處理相應的業務邏輯 -> 處理器返回一個模型檢視 ModelAndView -> 檢視解析器進行解析 -> 返回一個檢視物件->前端控制器 DispatcherServlet 渲染資料(Moder)->將得到檢視物件返回給使用者

如下圖所示:


640
SpringMVC執行原理


上圖的一個筆誤的小問題:Spring MVC 的入口函式也就是前端控制器 DispatcherServlet 的作用是接收請求,響應結果。

流程說明(重要):

(1)客戶端(瀏覽器)傳送請求,直接請求到 DispatcherServlet。

(2)DispatcherServlet 根據請求資訊呼叫 HandlerMapping,解析請求對應的 Handler。

(3)解析到對應的 Handler(也就是我們平常說的 Controller 控制器)後,開始由 HandlerAdapter 介面卡處理。

(4)HandlerAdapter 會根據 Handler 來呼叫真正的處理器開處理請求,並處理相應的業務邏輯。

(5)處理器處理完業務後,會返回一個 ModelAndView 物件,Model 是返回的資料物件,View 是個邏輯上的 View。

(6)ViewResolver 會根據邏輯 View 查詢實際的 View。

(7)DispaterServlet 把返回的 Model 傳給 View(檢視渲染)。

(8)把 View 返回給請求者(瀏覽器)

SpringMVC 重要元件說明

1、前端控制器DispatcherServlet(不需要工程師開發),由框架提供(重要)

作用:Spring MVC 的入口函式。接收請求,響應結果,相當於轉發器,中央處理器。有了 DispatcherServlet 減少了其它元件之間的耦合度。使用者請求到達前端控制器,它就相當於mvc模式中的c,DispatcherServlet是整個流程控制的中心,由它呼叫其它元件處理使用者的請求,DispatcherServlet的存在降低了元件之間的耦合性。

2、處理器對映器HandlerMapping(不需要工程師開發),由框架提供

作用:根據請求的url查詢Handler。HandlerMapping負責根據使用者請求找到Handler即處理器(Controller),SpringMVC提供了不同的對映器實現不同的對映方式,例如:配置檔案方式,實現介面方式,註解方式等。

3、處理器介面卡HandlerAdapter

作用:按照特定規則(HandlerAdapter要求的規則)去執行Handler
通過HandlerAdapter對處理器進行執行,這是介面卡模式的應用,通過擴充套件介面卡可以對更多型別的處理器進行執行。

4、處理器Handler(需要工程師開發)

注意:編寫Handler時按照HandlerAdapter的要求去做,這樣介面卡才可以去正確執行Handler
Handler 是繼DispatcherServlet前端控制器的後端控制器,在DispatcherServlet的控制下Handler對具體的使用者請求進行處理。
由於Handler涉及到具體的使用者業務請求,所以一般情況需要工程師根據業務需求開發Handler。

5、檢視解析器View resolver(不需要工程師開發),由框架提供

作用:進行檢視解析,根據邏輯檢視名解析成真正的檢視(view)
View Resolver負責將處理結果生成View檢視,View Resolver首先根據邏輯檢視名解析成物理檢視名即具體的頁面地址,再生成View檢視物件,最後對View進行渲染將處理結果通過頁面展示給使用者。 springmvc框架提供了很多的View檢視型別,包括:jstlView、freemarkerView、pdfView等。
一般情況下需要通過頁面標籤或頁面模版技術將模型資料通過頁面展示給使用者,需要由工程師根據業務需求開發具體的頁面。

6、檢視View(需要工程師開發)

View是一個介面,實現類支援不同的View型別(jsp、freemarker、pdf…)

注意:處理器Handler(也就是我們平常說的Controller控制器)以及檢視層view都是需要我們自己手動開發的。其他的一些元件比如:前端控制器DispatcherServlet、處理器對映器HandlerMapping、處理器介面卡HandlerAdapter等等都是框架提供給我們的,不需要自己手動開發。

DispatcherServlet詳細解析

首先看下原始碼:

package org.springframework.web.servlet;

@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {

    public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
    public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
    public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
    public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
    public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
    public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
    public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
    public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
    public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";
    public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
    public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";
    public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
    public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
    public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";
    public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";
    public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";
    public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION";
    public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
    private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
    protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
    private static final Properties defaultStrategies;
    static {
        try {
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
        }
    }

    /** Detect all HandlerMappings or just expect "handlerMapping" bean? */
    private boolean detectAllHandlerMappings = true;

    /** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */
    private boolean detectAllHandlerAdapters = true;

    /** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */
    private boolean detectAllHandlerExceptionResolvers = true;

    /** Detect all ViewResolvers or just expect "viewResolver" bean? */
    private boolean detectAllViewResolvers = true;

    /** Throw a NoHandlerFoundException if no Handler was found to process this request? **/
    private boolean throwExceptionIfNoHandlerFound = false;

    /** Perform cleanup of request attributes after include request? */
    private boolean cleanupAfterInclude = true;

    /** MultipartResolver used by this servlet */
    private MultipartResolver multipartResolver;

    /** LocaleResolver used by this servlet */
    private LocaleResolver localeResolver;

    /** ThemeResolver used by this servlet */
    private ThemeResolver themeResolver;

    /** List of HandlerMappings used by this servlet */
    private List<HandlerMapping> handlerMappings;

    /** List of HandlerAdapters used by this servlet */
    private List<HandlerAdapter> handlerAdapters;

    /** List of HandlerExceptionResolvers used by this servlet */
    private List<HandlerExceptionResolver> handlerExceptionResolvers;

    /** RequestToViewNameTranslator used by this servlet */
    private RequestToViewNameTranslator viewNameTranslator;

    private FlashMapManager flashMapManager;

    /** List of ViewResolvers used by this servlet */
    private List<ViewResolver> viewResolvers;

    public DispatcherServlet() {
        super();
    }

    public DispatcherServlet(WebApplicationContext webApplicationContext) {
        super(webApplicationContext);
    }
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
    }
}

DispatcherServlet類中的屬性beans:

  • HandlerMapping:用於handlers對映請求和一系列的對於攔截器的前處理和後處理,大部分用@Controller註解。

  • HandlerAdapter:幫助DispatcherServlet處理對映請求處理程式的介面卡,而不用考慮實際呼叫的是 哪個處理程式。- - -

  • ViewResolver:根據實際配置解析實際的View型別。

  • ThemeResolver:解決Web應用程式可以使用的主題,例如提供個性化佈局。

  • MultipartResolver:解析多部分請求,以支援從HTML表單上傳檔案。-

  • FlashMapManager:儲存並檢索可用於將一個請求屬性傳遞到另一個請求的input和output的FlashMap,通常用於重定向。

在Web MVC框架中,每個DispatcherServlet都擁自己的WebApplicationContext,它繼承了ApplicationContext。WebApplicationContext包含了其上下文和Servlet例項之間共享的所有的基礎框架beans。

HandlerMapping


640
HandlerMapping


HandlerMapping介面處理請求的對映HandlerMapping介面的實現類:

  • SimpleUrlHandlerMapping類通過配置檔案把URL對映到Controller類。

  • DefaultAnnotationHandlerMapping類通過註解把URL對映到Controller類。

HandlerAdapter


640
HandlerAdapter


HandlerAdapter介面-處理請求對映

AnnotationMethodHandlerAdapter:通過註解,把請求URL對映到Controller類的方法上。

HandlerExceptionResolver


640
HandlerExceptionResolver


HandlerExceptionResolver介面-異常處理介面

  • SimpleMappingExceptionResolver通過配置檔案進行異常處理。

  • AnnotationMethodHandlerExceptionResolver:通過註解進行異常處理。

ViewResolver


640
ViewResolver


ViewResolver介面解析View檢視。

UrlBasedViewResolver類 通過配置檔案,把一個檢視名交給到一個View來處理。

PS:如果覺得我的分享不錯,歡迎大家隨手點贊、轉發。

(完)

640?

Java團長

專注於Java乾貨分享

640

掃描上方二維碼獲取更多Java乾貨

相關文章