SpringBoot 整合 SpringMvc 原理探究

做者:koalajava

https://blog.csdn.net/believer123/java/article/details/70196572web


經過SpringBoot整合各個框架是愈來愈方便了,整合SpringMVC只須要添加對應的starer依賴便可。
spring


<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>


這樣,只須要根據自身需求,設置配置文件。啓動web服務器只須要運行java application就能夠了,再也不須要部署到tomcat服務了。tomcat


以前一直很好奇,使用SpringMVC時須要在web.xml上配置DispatcherServlet。而整合了SpringBoot後爲何就不須要配置了,下面就進行完整的分析。服務器


看着累?能夠直接看步驟7,核心分析。微信


一、尋找入口,找到WebServlet自動配置類:app


EmbeddedServletContainerAutoConfiguration框架


org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration

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


SpringBoot 自動配置功能類都以AutoConfiguration結尾koa


二、注入須要的Bean


從類上的註解能夠看出,導入了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));

            }
        }


實現ImportBeanDefinitionRegistrar接口,實現注入須要的Bean到Spring容器中,Mybatis(MapperScannerRegistrar)也是經過此接口來完成Mapper類的定義。


三、步驟2注入了bean:


EmbeddedServletContainerCustomizerBeanPostProcessor,該類實現了在ConfigurableEmbeddedServletContainer對象初始化前,進行行必要的參數配置。


  1. 獲取全部EmbeddedServletContainerCustomizer對象

  2. 調用EmbeddedServletContainerCustomizer.customize方法

  3. EmbeddedServletContainerCustomizer實現類根據自身需求設置WebServlet容器參數(如:端口號、鏈接數等等)

    

核心代碼以下:


@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容器的回調接口,在全部Bean初始化以前和以後分別回調此接口的postProcessBeforeInitialization,postProcessAfterInitialization方法。這樣就能夠根據需求在Bean初始化先後配置設置須要的功能。


經過步驟1-3完成了Web容器啓動前的參數配置功能。


四、EmbeddedWebApplicationContext入場Spring容器配置加載完成後,會回調EmbeddedWebApplicationContext.refresh方法。


EmbeddedWebApplicationContext在執行refresh方法中,調用了onRefresh方法進行ServletContainer配置。代碼以下:


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

    @Override
    protected void onRefresh() {
        ...省略
        //建立ServletContainer
        createEmbeddedServletContainer();
         ...省略
    }


EmbeddedWebApplicationContext實現了接口ConfigurableApplicationContext, Spring容器配置加載完成後會回調全部的ConfigurableApplicationContext對象的refresh方法。


1.在onRefresh方法中,獲取EmbeddedServletContainerFactory對象,由於工程上使用Tomcat,因此這裏就是TomcatEmbeddedServletContainerFactory


2.執行EmbeddedServletContainerFactory.getEmbeddedServletContainer方法


五、TomcatEmbeddedServletContainerFactory.getEmbeddedServletContainer這裏是Tomcat容器核心功能完的地方。主要完成了對Tomcat配置(不是這篇重點,省略代碼),在configureContext方法添加Tomcat容器啓動回調接口(重點)。


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容器啓動的一個回調接口。

    

在Tomcat啓動前,SpringBoot經過TomcatStarter完成Servlet,Filter等Web組件的組注入


六、TomcatStarter,在Tomcat啓動後回調onStartup。


七、EmbeddedWebApplicationContext在onStartup回調中完成SpringMvc功能注入


7.一、在selfInitialize方法中獲取到全部ServletContextInitializer對象,並調用其onStartup方法


7.二、ServletContextInitializer實現類以下:



7.三、經過上圖就很清楚的說明了Servlet,Filter等Web組件實現類


7.四、在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.五、這裏也就解釋了SpringBoot官方文檔70.1節上爲何是經過RegistrationBean添加Servlet與Filter的緣由了。



7.六、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一步一步的走下去,來查看內部實現。


總結


經過以上分析和Mybatis功能分析,發現滿滿的都是套路。在SpringBoot上實現自定義Starter功能應該都是以下套路:


    一、在自定義的XXAutoConfiguration上Import一個ImportBeanDefinitionRegistrar來注入指定Bean

    二、添加自定義的BeanPostProcessor在Bean初始化以前或以後完成配置功能或初始化某些依賴功能


END


我是武哥,最後給你們 免費分享我寫的 10 萬字 Spring Boot 學習筆記(帶完整目錄)以及對應的源碼 。這是我以前在 CSDN 開的一門課,因此筆記很是詳細完整,我準備將資料分享出來給你們免費學習,相信你們看完必定會有所收穫( 下面有下載方式 )。


能夠看出,我當時備課很是詳細,目錄很是完整,讀者能夠手把手跟着筆記,結合源代碼來學習。如今免費分享出來,有須要的讀者能夠下載學習,就在我公衆號回覆:筆記,就行。



若有文章對你有幫助,

在看轉發是對我最大的支持



關注Java開發寶典

天天學習技術乾貨



點贊是最大的支持 

本文分享自微信公衆號 - 武哥聊編程(eson_15)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索