SpringBoot 整合SpringMvc 原理探究(DispatchServlet添加流程)

通過SpringBoot整合各個框架是越來越方便了澈圈,整合SpringMVC只需要添加對應(yīng)的starer依賴即可。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

而且還配備了Tomcat的starter

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

這樣僚楞,只需要根據(jù)自身需求吗浩,設(shè)置配置文件。啟動web服務(wù)器只需要運行java application就可以了旷偿,不再需要部署到tomcat服務(wù)了。

之前一直很好奇爆侣,使用SpringMVC時需要在web.xml上配置DispatcherServlet萍程。而整合了SpringBoot后為什么就不需要配置了,下面就進行完整的分析兔仰。

看著累茫负?可以直接看步驟7,核心分析乎赴。

1忍法、尋找入口,找到WebServlet自動配置類:EmbeddedServletContainerAutoConfiguration

org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration{
    ...省略代碼
}

SpringBoot 自動配置功能類都以AutoConfiguration結(jié)尾

2榕吼、注入需要的Bean
從類上的注解可以看出饿序,導(dǎo)入了BeanPostProcessorsRegistrar,來添加EmbeddedServletContainerCustomizerBeanPostProcessor羹蚣。首先會查看工程是否有自定的EmbeddedServletContainerCustomizerBeanPostProcessor原探,如果沒有,則注入默認的EmbeddedServletContainerCustomizerBeanPostProcessor顽素。代碼如下:

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {
            if (this.beanFactory == null) {
                return;
            }
            if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(
    EmbeddedServletContainerCustomizerBeanPostProcessor.class, true,
                    false))) {
                registry.registerBeanDefinition(                    "embeddedServletContainerCustomizerBeanPostProcessor",
                        new RootBeanDefinition(
        EmbeddedServletContainerCustomizerBeanPostProcessor.class));
            }
            if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(
                    ErrorPageRegistrarBeanPostProcessor.class, true, false))) {
                registry.registerBeanDefinition("errorPageRegistrarBeanPostProcessor",
                        new RootBeanDefinition(
                                ErrorPageRegistrarBeanPostProcessor.class));

            }
        }

實現(xiàn)ImportBeanDefinitionRegistrar接口咽弦,實現(xiàn)注入需要的Bean到Spring容器中,Mybatis(MapperScannerRegistrar)也是通過此接口來完成Mapper類的定義胁出。

3型型、步驟2注入了bean:EmbeddedServletContainerCustomizerBeanPostProcessor,該類實現(xiàn)了在ConfigurableEmbeddedServletContainer對象初始化前全蝶,進行行必要的參數(shù)配置闹蒜。

  1. 獲取所有EmbeddedServletContainerCustomizer對象
  2. 調(diào)用EmbeddedServletContainerCustomizer.customize方法
  3. EmbeddedServletContainerCustomizer實現(xiàn)類根據(jù)自身需求設(shè)置WebServlet容器參數(shù)(如:端口號、連接數(shù)等等)
    核心代碼如下:
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
        if (bean instanceof ConfigurableEmbeddedServletContainer) {     postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
        }
        return bean;
    }
private void postProcessBeforeInitialization(ConfigurableEmbeddedServletContainer bean) {
        for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
            customizer.customize(bean);
        }
    }

ConfigurableEmbeddedServletContainer:是Web容器的接口裸诽,默認注入的有


這里寫圖片描述

BeanPostProcessor : 是Spring容器的回調(diào)接口嫂用,在所有Bean初始化之前和之后分別回調(diào)此接口的postProcessBeforeInitialization,postProcessAfterInitialization方法丈冬。這樣就可以根據(jù)需求在Bean初始化前后配置設(shè)置需要的功能嘱函。

通過步驟1-3完成了Web容器啟動前的參數(shù)配置功能。

4埂蕊、EmbeddedWebApplicationContext入場
Spring容器配置加載完成后往弓,會回調(diào)EmbeddedWebApplicationContext.refresh方法疏唾。EmbeddedWebApplicationContext在執(zhí)行refresh方法中,調(diào)用了onRefresh方法進行ServletContainer配置函似。代碼如下:

@Override
    public final void refresh() throws BeansException, IllegalStateException {
             ...省略
            super.refresh();
             ...省略
    }

    @Override
    protected void onRefresh() {
        ...省略
        //創(chuàng)建ServletContainer
        createEmbeddedServletContainer();
         ...省略
    }

EmbeddedWebApplicationContext實現(xiàn)了接口ConfigurableApplicationContext,Spring容器配置加載完成后會回調(diào)所有的ConfigurableApplicationContext對象的refresh方法槐脏。

  1. 在onRefresh方法中,獲取EmbeddedServletContainerFactory對象撇寞,因為工程上使用Tomcat顿天,所以這里就是TomcatEmbeddedServletContainerFactory
  2. 執(zhí)行EmbeddedServletContainerFactory.getEmbeddedServletContainer方法

5、TomcatEmbeddedServletContainerFactory.getEmbeddedServletContainer這里是Tomcat容器核心功能完的地方蔑担。主要完成了對Tomcat配置(不是這篇重點牌废,省略代碼),在configureContext方法添加Tomcat容器啟動回調(diào)接口(重點)啤握。

protected void configureContext(Context context,
            ServletContextInitializer[] initializers) {
        TomcatStarter starter = new TomcatStarter(initializers);
        if (context instanceof TomcatEmbeddedContext) {
            // Should be true
            ((TomcatEmbeddedContext) context).setStarter(starter);
        }
        ...省略
    }

ServletContainerInitializer是Tomcat容器啟動的一個回調(diào)接口鸟缕。
在Tomcat啟動前,SpringBoot通過TomcatStarter完成Servlet排抬,F(xiàn)ilter等Web組件的組注入

6懂从、TomcatStarter,在Tomcat啟動后回調(diào)onStartup蹲蒲。

7番甩、EmbeddedWebApplicationContext在onStartup回調(diào)中完成SpringMvc功能注入

7.1、在selfInitialize方法中獲取到所有ServletContextInitializer對象悠鞍,并調(diào)用其onStartup方法对室,
7.2、ServletContextInitializer實現(xiàn)類如下:


這里寫圖片描述

7.3咖祭、通過上圖就很清楚的說明了Servlet,F(xiàn)ilter等Web組件實現(xiàn)類
7.4蔫骂、在ServletRegistrationBean向ServletContainer添加Servlet

@Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        Assert.notNull(this.servlet, "Servlet must not be null");
        String name = getServletName();
        if (!isEnabled()) {
            logger.info("Servlet " + name + " was not registered (disabled)");
            return;
        }
        logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);
        Dynamic added = servletContext.addServlet(name, this.servlet);
        if (added == null) {
            logger.info("Servlet " + name + " was not registered "
                    + "(possibly already registered?)");
            return;
        }
        configure(added);
    }

7.5么翰、這里也就解釋了SpringBoot官方文檔70.1節(jié)上為什么是通過RegistrationBean添加Servlet與Filter的原因了。


這里寫圖片描述

7.6辽旋、DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration此處添加SpringMVC核心功能類DispatcherServlet

    @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public ServletRegistrationBean dispatcherServletRegistration(
                DispatcherServlet dispatcherServlet) {
            ServletRegistrationBean registration = new ServletRegistrationBean(
                    dispatcherServlet, this.serverProperties.getServletMapping());
            registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
            registration.setLoadOnStartup(
                    this.webMvcProperties.getServlet().getLoadOnStartup());
            if (this.multipartConfig != null) {
                registration.setMultipartConfig(this.multipartConfig);
            }
            return registration;
        }

以上只是將重要點抽出來說明浩嫌,貼上全部源碼也是無意義的。要理解其中過程還需要自行查看源碼补胚。

通過以上步驟分析了SpringBoot集成SpringMVC和Tomcat功能簡要步驟码耐。其實只要找到了入口,即可Debug一步一步的走下去溶其,來查看內(nèi)部實現(xiàn)骚腥。


總結(jié)

通過以上分析和Mybatis功能分析,發(fā)現(xiàn)滿滿的都是套路瓶逃。在SpringBoot上實現(xiàn)自定義Starter功能應(yīng)該都是如下套路:
1束铭、在自定義的XXAutoConfiguration上Import一個ImportBeanDefinitionRegistrar來注入指定Bean
2廓块、添加自定義的BeanPostProcessor在Bean初始化之前或之后完成配置功能或初始化某些依賴功能

以上代碼分析來自我的工程SpringBootLearning
SpringBootLean 是對springboot學(xué)習(xí)與研究項目,是根據(jù)實際項目的形式對進行配置與處理契沫,歡迎star與fork带猴。
[oschina 地址]
http://git.oschina.net/cmlbeliever/SpringBootLearning
[github 地址]
https://github.com/cmlbeliever/SpringBootLearning

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市懈万,隨后出現(xiàn)的幾起案子拴清,更是在濱河造成了極大的恐慌,老刑警劉巖会通,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贷掖,死亡現(xiàn)場離奇詭異,居然都是意外死亡渴语,警方通過查閱死者的電腦和手機苹威,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驾凶,“玉大人牙甫,你說我怎么就攤上這事〉魑ィ” “怎么了窟哺?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長技肩。 經(jīng)常有香客問我且轨,道長,這世上最難降的妖魔是什么虚婿? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任旋奢,我火速辦了婚禮,結(jié)果婚禮上然痊,老公的妹妹穿的比我還像新娘至朗。我一直安慰自己,他們只是感情好剧浸,可當(dāng)我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布锹引。 她就那樣靜靜地躺著,像睡著了一般唆香。 火紅的嫁衣襯著肌膚如雪嫌变。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天躬它,我揣著相機與錄音腾啥,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛碑宴,可吹牛的內(nèi)容都是我干的软啼。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼延柠,長吁一口氣:“原來是場噩夢啊……” “哼祸挪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起贞间,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤贿条,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后增热,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體整以,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年峻仇,在試婚紗的時候發(fā)現(xiàn)自己被綠了公黑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡摄咆,死狀恐怖凡蚜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吭从,我是刑警寧澤朝蜘,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站涩金,受9級特大地震影響谱醇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜步做,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一副渴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辆床,春花似錦佳晶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽中跌。三九已至咨堤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間漩符,已是汗流浹背一喘。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凸克。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓议蟆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親萎战。 傳聞我的和親對象是個殘疾皇子咐容,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,807評論 6 342
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)蚂维,斷路器戳粒,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 今天是訓(xùn)練營的第二天,還是那些時間虫啥,每天還是那些必須的事蔚约,伴隨著昨天的開營,自己就有一種新鮮涂籽,好奇苹祟,緊迫感!早上睜...
    真誠永恒閱讀 246評論 0 0
  • 我渴念過善良 我不善良 我也沒說评雌,我將善良 眼色冷慣了 才容易成活
    蘿卜衣閱讀 289評論 0 1
  • 優(yōu)化主標題(模板相對年輕與所講主題有出入) 個人照去底色會更融合 自我介紹內(nèi)容重點不突出 字體可全部選用白色 可縮...
    松林walter閱讀 228評論 0 0