Spring Boot系列(四):Spring Boot原始碼解析

toby.xu發表於2020-08-21

一、自動裝配原理

  之前博文已經講過,@SpringBootApplication繼承了@EnableAutoConfiguration,該註解匯入了AutoConfigurationImport Selector,這個類主要是掃描spring-boot-autoconfigure下面的META-INF\spring.factories中的EnableAutoConfiguration對應的全類名,其中XXXAutoConfiguration都是一個個自動配置類。

   自動裝配原理具體參考:Spring Boot系列(二):Spring Boot自動裝配原理解析

二、Spring Boot的jar啟動

  1、Spring Boot自動裝配Tomcat元件

  ① EmbeddedWebServerFactoryCustomizerAutoConfiguration內嵌的Web容器工廠定製器自動裝配類,裝配了TomcatWebServerFactoryCustomizer元件

   Tomcat工廠定製器TomcatWebServerFactoryCustomizer用來設定容器的屬性,把ServerProperties中的屬性設定到Tomcat容器的工廠中。

   ServerProperties服務的屬性類:

  ② ServletWebServerFactoryAutoConfiguration,ServletWeb工廠自動裝配類,裝配了如下四個元件

  • ServletWebServerFactoryCustomizer:用來定製ServletWeb服務工廠
  • TomcatServletWebServerFactoryCustomizer:用來定製TomcatServletWeb服務工廠
  • ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar :後置處理器
  • ServletWebServerFactoryConfiguration:用來配置TomcatServletWeb服務工廠

   2、SpringApplication.run啟動流程

  ① new SpringApplication(primarySources),建立了一個SpringApplication

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        //設定主配置類 我們自己寫的Spring Boot的啟動類
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //設定web應用的型別
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //設定容器初始化器(ApplicationContextInitializer型別的)
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //把監聽器設定到SpringApplication中[ApplicationListener]
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //設定主配置類
        this.mainApplicationClass = deduceMainApplicationClass();
    }

  ② SpringApplication的run方法:

  主要流程:

  第一:建立容器物件

  第二:去META-INFO/spring.factories中獲取SpringApplicationRunListener監聽器(事件釋出監聽器)

  第三:釋出容器starting事件(通過spring的事件多播器)

  第四:封裝命令列引數

  第五:準備容器環境

  第六:列印Springboot的圖示

  第七:根據webApplicationType來建立容器

  第八:準備容器上下文

  第九:釋出容器啟動事件

  第十:釋出容器執行事件

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        //容器物件
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        //去META-INFO/spring.factories中獲取SpringApplicationRunListener監聽器(事件釋出監聽器)
        SpringApplicationRunListeners listeners = getRunListeners(args);
        //釋出容器starting事件(通過spring的事件多播器)
        listeners.starting();
        try {
            //封裝命令列引數
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            /**
             * 準備容器環境
             * 1: 獲取或者建立環境
             * 2:把命令列引數設定到環境中
             * 3:通過監聽器釋出環境準備事件
             */
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            //列印Springboot的圖示
            Banner printedBanner = printBanner(environment);
            //建立容器根據webApplicationType來建立容器(通過反射建立)
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            /**
             * 準備上下文
             * 1:把環境設定到容器中
             * 2: 迴圈呼叫ApplicationContextInitializer進行容器初始化工作
             * 3: 釋出容器上下文準備完成事件
             * 4: 註冊關於Springboot特性的相關單例Bean
             * 5: 釋出容器上下文載入完畢事件
             */
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            //執行ApplicationRunner和CommandLineRunner
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            //釋出容器啟動事件
            listeners.started(context);
            //執行ApplicationRunner和CommandLineRunner
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            //出現異常呼叫異常分析保護類進行分析
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            //釋出容器執行事件
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

  ③ org.springframework.boot.SpringApplication#refreshContext

  ④ org.springframework.boot.SpringApplication#refresh

  ⑤ org.springframework.context.support.AbstractApplicationContext#refresh

  到了AbstractApplicationContext#refresh方法,之前講過Spring IoC原始碼解析講過該方法的12大步,這裡就不細說,詳細可以參考:Spring系列(三):Spring IoC原始碼解析,裡面說過有一步就是onRefresh(),這個方法預設是空的,由子類根據自身需要去實現

  ⑥ org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh

  該onRefresh方法分2步

  第一:super.onRefresh(); 呼叫父類的onRefresh()

  第二:createWebServer();建立Web服務,很重要,很重要,很重要!!!

  ⑦ createWebServer()方法

  第一:ServletContext servletContext = getServletContext(); 獲取Servlet的上下文

  第二:ServletWebServerFactory factory = getWebServerFactory();獲取Tomcat的Web服務工廠

  第三:this.webServer = factory.getWebServer(getSelfInitializer()); 建立一個Web伺服器

   ⑧ TomcatServletWebServerFactory#getWebServer()方法,主要用於建立一個Tomcat Web容器

   到此我們知道Spring Boot的啟動通過Spring IoC的refresh中的的onRefresh()帶動了Tomcat的啟動,跟我們之前我們學Spring Mvc的時候剛好相反,Spring Mvc的是Tomcat的啟動帶動了Spring容器的啟動;

三、普通Web工程啟動

  1、普通的web工程,我們找到web.xml,會發現都配置瞭如下的載入Spring的配置。

   2、Tomcat啟動的時候會呼叫該上下文載入的的監聽器的contextInitialized方法,我們進入到該方法:

   3、進入初始化Web應用上下文initWebApplicationContext方法中:

  • this.context = createWebApplicationContext(servletContext);
  • configureAndRefreshWebApplicationContext(cwac, servletContext);

  4、進去到configureAndRefreshWebApplicationContext(cwac, servletContext)方法中:

   5、進入到refresh方法實際就到了org.springframework.context.support.AbstractApplicationContext#refresh的方法

  這個方法很熟悉了,Spring IoC的refresh的12大步;

四、Spring Boot啟動流程圖

相關文章