Spring MVC之組件初始化

1.Spring MVC整體結構介紹

Spring MVC主要類結構圖

image

從上圖中可以看到在Servlet的繼承結構中一共有5個類油够,分別是Java提供的兩個類GenericServlet,HttpServlet和Spring MVC提供的三個類HttpServletBean蜗细、FrameworkServlet和Dispat
cherServlet

Spring MVC請求流程圖


image

從上圖可以看出DispatcherServlet是Spring MVC框架的核心瓮具,通過DispatcherServlet獲取處理請求的Handler
Adapter(處理請求的具體Controller),然后將返回的結果交給ViewResolver(處理視圖的類),然后渲染出視圖返回給客戶端

2.HttpServletBean

通過前面的文章晨雳,我們知道搭建SpringMVC框架的時候在web.xml文件中配置了DispatcherServlet,它是Spring MVC控制器的核心奸腺,當系統(tǒng)開始運行時餐禁,DispatcherServlet就開始初始化(<load-on-startup>1</load-on-startup>),由于DispatcherServlet類本身沒有init初始化方法突照,它的父類中只有HttpServletBean中有inti初始化方法,HttpServletBean的初始化方法如下:

    public final void init() throws ServletException {
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Initializing servlet \'" + this.getServletName() + "\'");
        }

        try {
            //將Servlet中配置的參數封裝到ex變量中帮非,requiredProperties為必須參數,如果沒有配置則報異常
            HttpServletBean.ServletConfigPropertyValues ex = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
            BeanWrapper bw =PropertyAccessorFactory.forBeanPropertyAccess(this);
            ServletContextResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
            bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
            //模板方法讹蘑,調用的是FrameworkServlet中的初始化方法末盔,做一些初始化工作。bw代表DispatcherServlet
            this.initBeanWrapper(bw);
            //將配置的初始化值(如ContextConfigLocation)設置到DispatcherServlet中
            bw.setPropertyValues(ex, true);
        } catch (BeansException var4) {
            this.logger.error("Failed to set bean properties on servlet \'" + this.getServletName() + "\'", var4);
            throw var4;
        }
        //模板方法座慰,FrameworkServlet初始化的入口方法
        this.initServletBean();
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet \'" + this.getServletName() + "\' configured successfully");
        }

    }
HttpServletBean.ServletConfigPropertyValues ex = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
//getServletConfig()方法是Servlet接口提供的方法用于獲取ServletConfig陨舱,當Servlet的init方法被調用時,會接受到一個ServletConfig類型的參數版仔,指的是Servlet的配置游盲,在web.xml中定義Servlet時通過init-param標簽配置的參數就是通過ServletConfig保存的。

BeanWrapper 是Spring提供的一個用來操作JavaBean屬性的工具蛮粮,使用它可以直接修改一個對象的屬性

BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);  
//通過PropertyAccessorFactory對象的forBeanPropertyAccess方法將DispatcherServlet封裝成BeanWrapper對象益缎,這樣通過bw變量就可以對DispatcherServlet的屬性進行操作
3.FrameworkServlet

從上面HttpServletBean中的初始化代碼得知,FrameworkServlet的初始化入口方法是initServletBean

    protected final void initServletBean() throws ServletException {
        this.getServletContext().log("Initializing Spring FrameworkServlet \'" + this.getServletName() + "\'");
        if(this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet \'" + this.getServletName() + "\': initialization started");
        }

        long startTime = System.currentTimeMillis();

        try {
            //初始化WebApplicationContext
            this.webApplicationContext = this.initWebApplicationContext();
            //初始化FrameworkServlet
            this.initFrameworkServlet();
        } catch (ServletException var5) {
            this.logger.error("Context initialization failed", var5);
            throw var5;
        } catch (RuntimeException var6) {
            this.logger.error("Context initialization failed", var6);
            throw var6;
        }

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

    }

首先查看WebApplicationContext初始化代碼然想;WebApplicationContext是web應用的上下文環(huán)境

  protected WebApplicationContext initWebApplicationContext() {
        //通過getServletContext獲取的ServletContext來獲取根上下文
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        WebApplicationContext wac = null;
        //如果已經通過構造方法設置了webApplicationContext
        if(this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if(wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext attrName = (ConfigurableWebApplicationContext)wac;
                if(!attrName.isActive()) {
                    if(attrName.getParent() == null) {
                        attrName.setParent(rootContext);
                    }

                    this.configureAndRefreshWebApplicationContext(attrName);
                }
            }
        }

        if(wac == null) {
            //如果webApplicationContext已經存在ServletContext中時莺奔,通過配置在Servlet中的參數獲取
            wac = this.findWebApplicationContext();
        }

        if(wac == null) {
            //創(chuàng)建一個新的webApplicationContext
            wac = this.createWebApplicationContext(rootContext);
        }

        if(!this.refreshEventReceived) {
            //模板方法,HttpServletBean中用來初始化Spring MVC的九大組件
            this.onRefresh(wac);
        }

        if(this.publishContext) {
            //將ApplicationContext保存到ServletContext中
            String attrName1 = this.getServletContextAttributeName();
            this.getServletContext().setAttribute(attrName1, wac);
            if(this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet \'" + this.getServletName() + "\' as ServletContext attribute with name [" + attrName1 + "]");
            }
        }

        return wac;
    }
    

獲取spinrg的根容器rootContext
WebApplicationContext rootContext=WebApplicationContextUtils.getWebApplicationContext(this.getServl
etContext());

設置webApplicationContext并根據情況調用onRefresh方法

設置webApplicationContext的三種方法

  • 在構造方法中已經傳遞了webApplicationContext參數变泄,這時只需要對其進行一些設置即可
  • webApplicationContext已經在ServletContext中了弊仪,這時只需要在配置Servlet的時候將ServletContext中的webApplicationContext的name配置到contextAttribute屬性就可以了
  • 前面兩種方式都無效的情況下熙卡,通過createWebApplicationContext來創(chuàng)建一個

創(chuàng)建webApplicationContext的方法

protected WebApplicationContext createWebApplicationContext(
ApplicationContext parent) {
        //通過getContextClass獲取要創(chuàng)建的類型
        Class contextClass = this.getContextClass();
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet with name \'" + this.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 \'" + this.getServletName() + "\': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
        } else {
            //具體創(chuàng)建
            ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
            wac.setEnvironment(this.getEnvironment());
            wac.setParent(parent);
            //將設置的contextConfigLocation參數傳給wac,默認傳入WEB-INF/[ServletName]-servlet.xml
            wac.setConfigLocation(this.getContextConfigLocation());
            this.configureAndRefreshWebApplicationContext(wac);
            return wac;
        }
    }

將webApplicationContext設置到ServletContext中

    if(this.publishContext) {
        //將ApplicationContext保存到ServletContext中
        String attrName1 = this.getServletContextAttributeName();
        this.getServletContext().setAttribute(attrName1, wac);
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Published WebApplicationContext of servlet \'" + this.getServletName() + "\' as ServletContext attribute with name [" + attrName1 + "]");
        }
    }

根據publishContext標志判斷是否將webApplicationContext設置到ServletContext的屬性中励饵,publishContext標志可以在配置Servlet時通過init-param參數進行設置驳癌,HttpServletBean初始化時會將其設置到publishContext參數

3.DispatcherServlet

onRefresh方法是DispatcherServlet的入口方法

    protected void onRefresh(ApplicationContext context) {
        this.initStrategies(context);
    }

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

onRefresh方法調用了initStrategies方法,initStrategies方法中初始化了Spring MVC九大組件

4.小結

上面主要分析了Spring MVC自身的創(chuàng)建過程役听,Spring MVC中的Servlet一共分為三個層
次颓鲜,分別是HttpServletBean、FrameworkServlet和DispatcherServlet典予。

  • HttpServletBean繼承自Java的HttpServlet甜滨,其作用是將Servlet中配置的參數設置到相應的屬性
  • FrameworkServlet初始化了WebApplicationContext
  • DispatcherServlet初始化了自身的9大組件
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瘤袖,隨后出現的幾起案子衣摩,更是在濱河造成了極大的恐慌,老刑警劉巖捂敌,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件艾扮,死亡現場離奇詭異,居然都是意外死亡占婉,警方通過查閱死者的電腦和手機泡嘴,發(fā)現死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逆济,“玉大人酌予,你說我怎么就攤上這事〗被牛” “怎么了抛虫?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長简僧。 經常有香客問我建椰,道長,這世上最難降的妖魔是什么涎劈? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮阅茶,結果婚禮上蛛枚,老公的妹妹穿的比我還像新娘。我一直安慰自己脸哀,他們只是感情好蹦浦,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著撞蜂,像睡著了一般盲镶。 火紅的嫁衣襯著肌膚如雪侥袜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天溉贿,我揣著相機與錄音枫吧,去河邊找鬼。 笑死宇色,一個胖子當著我的面吹牛九杂,可吹牛的內容都是我干的。 我是一名探鬼主播宣蠕,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼例隆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了抢蚀?” 一聲冷哼從身側響起镀层,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎皿曲,沒想到半個月后唱逢,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡谷饿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年惶我,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片博投。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡绸贡,死狀恐怖,靈堂內的尸體忽然破棺而出毅哗,到底是詐尸還是另有隱情听怕,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布虑绵,位于F島的核電站尿瞭,受9級特大地震影響,放射性物質發(fā)生泄漏翅睛。R本人自食惡果不足惜声搁,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捕发。 院中可真熱鬧疏旨,春花似錦、人聲如沸扎酷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谁榜,卻和暖如春幅聘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背窃植。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工帝蒿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人撕瞧。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓陵叽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親丛版。 傳聞我的和親對象是個殘疾皇子巩掺,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355