1. 程式人生 > >SpringMVC初始化過程

SpringMVC初始化過程

ps:以下所有原始碼都是spring4.2.4版本
springmvc是主流的MVC框架,我先從使用開始一步一步解析其初始化過程,springmvc的核心是DispatcherServlet,它是前端控制器,負責攔截客戶端發過來的請求,然後解析請求進行分發。
DispatcherServlet是基於Servlet的,所以使用springmvc先在web.xml中配置DispatcherServlet

<!-- 配置DisaptcherServlet -->
    <servlet>
        <servlet-name>springMVC</servlet-name
>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 初始化引數,配置springmvc配置檔案 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springMVC.xml</param-value
>
</init-param> <!-- web容器啟動時載入該Servlet --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping
>

配置檔案解析:可以看到在web.xml配置了一個名叫DispatcherServlet的Servlet,所有的請求都會在它那裡執行,並且它有一個名叫contextConfigLocation的引數,在這裡賦值為classpath:springMVC.xml(即mvc的配置檔案位置所在)
接下來看DispatcherServlet主要的繼承關係
這裡寫圖片描述
圖中每個類中的方法是待會分析初始化過程會呼叫的主要方法,可以看到DispatcherServlet本質還是一個Servlet,上面說到在web容器啟動時會會載入DispatcherServlet,每個Servlet在第一次載入時都會呼叫其init()方法,但是DispatcherServlet本身沒有這個方法,所以系統會去它父類尋找init()方法,最後在HttpServletBean找到,呼叫,以下是init()方法原始碼

1.HttpServletBean的init()方法

//DispatcherServlet第一次載入時呼叫init方法
@Override
    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // Set bean properties from init parameters.
        /**
         *概括來說:
         *try語句塊的作用就是獲取剛剛在web.xml配置的初始化引數<init-param>
         *並將這些引數設定到DispatcherServlet中
         */
        try {
            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
            ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
            initBeanWrapper(bw);
            bw.setPropertyValues(pvs, true);
        }
        catch (BeansException ex) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            throw ex;
        }

        // Let subclasses do whatever initialization they like.
        //模版方法,此方法在HttpServletBean本身是空的,但是因為呼叫方法的物件是DispatcherServlet
        //所以優先在DispatcherServlet找,找不到再去父類找,最後在FrameworkServlet找到
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }

總結HttpServletBean的作用:

  • 獲取web.xml的中配置DispatcherServlet的初始化引數,存放到一個引數容器ServletConfigPropertyValues中
  • 根據傳進來的this建立物件包 裹者(BeanWrapper),本質上它就是DispatcherServlet
  • 最後通過bw.setPropertyValues(pvs, true);把引數設定到bw(即DispatcherServlet)裡面去,最後呼叫子類的initServletBean()

2.FrameworkServlet的initServletBean()方法原始碼

@Override
    protected final void initServletBean() throws ServletException {
        getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
        }
        long startTime = System.currentTimeMillis();

        try {
            //重要程式碼,建立springmvc的ioc容器例項
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();
        }
        catch (ServletException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }
        catch (RuntimeException ex) {
            this.logger.error("Context initialization failed", ex);
            throw ex;
        }

        if (this.logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                    elapsedTime + " ms");
        }
    }

看到程式碼中通過initWebApplicationContext()就返springMVC的容器例項了,接下來看initWebApplicationContext()原始碼

protected WebApplicationContext initWebApplicationContext() {
        //首先通過ServletContext獲得spring容器,因為子容器springMVC要和父容器spring容器進行關聯
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        //定義springMVC容器wac
        WebApplicationContext wac = null;

        //判斷容器是否由程式設計式傳入(即是否已經存在了容器例項),存在的話直接賦值給wac,給springMVC容器設定父容器
        //最後呼叫重新整理函式configureAndRefreshWebApplicationContext(wac),作用是把springMVC.xml的配置資訊載入到容器中去
        if (this.webApplicationContext != null) {
            // A context instance was injected at construction time -> use it
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {

                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent -> set
                        // the root application context (if any; may be null) as the parent
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            // 在ServletContext中尋找是否有springMVC容器,初次執行是沒有的,springMVC初始化完畢ServletContext就有了springMVC容器
            //具體原因看下面46行程式碼
            wac = findWebApplicationContext();
        }

        //當wac既沒有沒被程式設計式註冊到容器中的,也沒在ServletContext找得到,此時就要新建一個springMVC容器
        if (wac == null) {
            // 建立springMVC容器
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            //到這裡mvc的容器已經建立完畢,接著才是真正呼叫DispatcherServlet的初始化方法onRefresh(wac)
            onRefresh(wac);
        }

        if (this.publishContext) {
            //將springMVC容器存放到ServletContext中去,方便下次取出來
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                        "' as ServletContext attribute with name [" + attrName + "]");
            }
        }

        return wac;
    }

接下來看建立springMVC 的ioc容器方法createWebApplicationContext(WebApplicationContext parent)

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        Class<?> contextClass = getContextClass();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet with name '" + getServletName() +
                    "' will try to create custom WebApplicationContext context of class '" +
                    contextClass.getName() + "'" + ", using parent context [" + parent + "]");
        }
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException(
                    "Fatal initialization error in servlet with name '" + getServletName() +
                    "': custom WebApplicationContext class [" + contextClass.getName() +
                    "] is not of type ConfigurableWebApplicationContext");
        }
        //例項化空白的ioc容器
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
        //給容器設定環境
        wac.setEnvironment(getEnvironment());
        //給容器設定父容器(就是spring容器),兩個ioc容器關聯在一起了
        wac.setParent(parent);
        //給容器載入springMVC.xml的配置資訊,bean注入,註解,掃描等等
        wac.setConfigLocation(getContextConfigLocation());
        //上面提到過這方法,重新整理容器,根據springMVC.xml配置檔案完成初始化操作,此時springMVC容器建立完成
        configureAndRefreshWebApplicationContext(wac);

        return wac;
    }

以上就是FrameworkServlet建立容器的過程
總結FrameworkServlet作用:

  • 建立springMVC的ioc容器根據配置檔案例項化裡面各種bean,並將之與spring的ioc容器進行關聯
  • 把創建出來的mvc容器存放到ServletContext中

接下來看DispatcherServlet的onRefresh(ApplicationContext context)方法

3.DispatcherServlet的onRefresh(ApplicationContext context)方法

@Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

很簡單,呼叫了initStrategies(context),初始化策略元件

protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);//檔案上傳解析
        initLocaleResolver(context);//本地解析
        initThemeResolver(context);//主題解析
        initHandlerMappings(context);//url請求對映
        initHandlerAdapters(context);//初始化真正呼叫controloler方法的類
        initHandlerExceptionResolvers(context);//異常解析
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);//檢視解析
        initFlashMapManager(context);
    }

至此,整個springMVC初始化的過程就結束了

總結

  1. web應用啟動時掃描web.xml檔案,掃描到DispatcherServlet,對其進行初始化
  2. 呼叫DispatcherServlet父類的父類HttpServletBean的init()方法,把配置DispatcherServlet的初始化引數設定到DispatcherServlet中,呼叫子類FrameworkServlet的initServletBean()方法
  3. initServletBean()建立springMVC容器例項並初始化容器,並且和spring父容器進行關聯,使得mvc容器能訪問spring容器裡面的bean,之後呼叫子類DispatcherServlet的onRefresh(ApplicationContext context)方法
  4. onRefresh(ApplicationContext context)進行DispatcherServlet的策略元件初始化工作,url對映初始化,檔案解析初始化,執行介面卡初始化等等。

到此文章已經寫完了,如果出現錯誤,請指出,十分感謝你的指教。

相關推薦

SpringMVC初始過程

ps:以下所有原始碼都是spring4.2.4版本 springmvc是主流的MVC框架,我先從使用開始一步一步解析其初始化過程,springmvc的核心是DispatcherServlet,它是前端控制器,負責攔截客戶端發過來的請求,然後解析請求進行分發。

spring原始碼(一) springmvc啟動過程springmvc初始過程

spring mvc配置 我們知道要想使用springmvc,一般需要配置如下 web.xml中配置ContextLoaderListener來載入spring根配置檔案。 <web-app> <context-param&g

springMVC啟動初始過程

文件的 web.xml info 容器 環境 創建 con inf bubuko 在web.xml裏配置分發servlet和ContextLoaderListener,ContextLoaderListener是spring提供的類,它繼承了ContextLoader類和實

SpringMVC之淺析元件初始過程

/** Well-known name for the MultipartResolver object in the bean factory for this namespace. */ public static final String MULTIPART_RESOLVER_BEAN_NAME =

對象初始過程

加載 空間 分配 父類 對象初始化 默認 ont 方法 賦值 第一步、在創建類之前,檢查類是否已加載(檢查硬盤上的.class文件是否加載到內存中),如果沒有加載就先加載父類的文件,在加載本類的文件。java使用的加載的策略:懶惰式加載(按需加載),用到的時候加載,只加載一

JAVA中對象創建和初始過程

2.3 人的 cin 類型變量 認識 handle product window blank 1.Java中的數據類型   Java中有3個數據類型:基本數據類型(在Java中,boolean、byte、short、int、long、char、float、double這八種

Spring-IOC源碼解讀2-容器的初始過程

創建 對象 配置文件 instance tee rem leg source lag 1. IOC容器的初始化過程:IOC容器的初始化由refresh()方法啟動,這個啟動包括:BeanDifinition的Resource定位,加載和註冊三個過程。初始化的過程不包含Bea

openWRT自學---初始過程和主要腳本的分析--轉

系統重啟 usr 啟動服務 流程 amba oop comment functions 又是 參考文檔: http://wiki.openwrt.org/doc/techref/process.boot http://blog.csdn.net/jk110333/artic

JAVA-初步認識-第十章-對象的初始過程

兩個 而且 如果 image dos super() 就是 images logs 一. 現在簡單地將內存圖解簡單地串一串,從之前的到現在的全部組合起來講述一下, 編譯運行的結果為9,按照我們之前的認識來看,對象先是默認初始化,然後顯示初始化,最後是構造器初始化,這是從我

springmvc初始數據

java springmvc initializingbean 在使用springmvc時,我們也會在項目啟動時初始化一些數據,具體的方式見下面的鏈接。這裏我只貼一下InitializingBean的例子。註意事項:springmvc和sping整合時,配置註解的註意事項!不註意會導致我們的con

java中一個對象的初始過程

關系 col 中一 show 父類 style 覆蓋 結果 pan // 對象初始化的過程 class Fu { int num=55;// 1, num=0;對象中的成員變量默認初始化。5, 顯示初始化 num=55 { S

對象初始過程與單例設計模式(餓漢式與懶漢式)

得到 延時 兩個 都是 person 其他 導致 最大 類屬性 1.對象初始化過程:(先加載類到內存,然後加載類屬性,成員方法)    定義一個類Person, 在new Person("zhangsan",20); 初始化過程: 因為new 用到Person.clas

Person對象的初始過程

對象 默認 代碼 on() 沒有 第一步 類初始化 堆內存 找到 Person p = new Person();第一步:先在棧內存中開辟空間 p第二步:因為 new 用到了 Person.class 所以會先到硬盤中找到 Person.class 文件,通過jvm加載到內

spring源碼分析之初始過程

源碼分析 true singleton 存在 factory 源碼 org 包含 eric 1.org.springframework.web.context.ContextLoaderListener 一個ServletContextListener,web容器啟動監聽器

Mybatis在spring容器中的初始過程

tsql tor 懶加載 alt onf mes text batis mybatis 由servlet容器調用容器啟動監聽器 spring的applicationContext刷新 實例化所有的單例(非懶加載的) beanNames:所有的單例(非懶加載的)的id,

二、Java面向對象(8)_繼承思想——子類初始過程

AR args col color 所有 通過 子類初始化 anim turn 2018-05-01 子類初始化過程 構造器的調用遵循以下的順序: (1):調用基類構造器。這個步驟會不斷地反復遞歸下去,首先是構造這種層次結構的根,然後是下一層導出類,等等,直到最低層

手把手,嘴對嘴,講解UCOSII嵌入式操作系統的初始過程(二)

同學 save sam 嵌入式操作系統 相關信息 trie allow 狀態 cos 本章重點講解空閑任務的建立過程。 任務建立函數定義如下: 1 INT8U OSTaskCreate (void (*task)(void *p_arg), 2

Spring初始過程到AOP

should aop amp 切點 reg 遞歸調用 finish pes auto 初始化過程 public void refresh() throws BeansException, IllegalStateException { synchro

MyBatis初始過程解析----廣西11選5平臺出租源碼解析

solver 原本 file code 1.3 lds elements ret variables 準備工作 為了看清楚廣西11選5平臺出租的 Q1446595067 整個初始化過程,先創建一個簡單的Java項目,目錄結構如下圖所示: 1.1 Product 產品實體類

spring MVC初始過程學習筆記1

load cati 過程 mage 筆記 ngx 名稱 spring -s 如果有錯誤請指正~ 1.springmvc容器和spring的關系? 1.1 spring是個容器,主要是管理bean,不需要servlet容器就可以啟動,而springMVC實現了servl