SpringBoot2源碼2-核心啟動(dòng)過(guò)程和run方法

1. SpringBoot中怎么啟動(dòng)Tomcat?

1.1 ServletWebServerFactoryAutoConfiguration

配置Servlet web容器高诺。

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
      ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
      ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
      ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
}

ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class 導(dǎo)入 WebServerFactoryCustomizerBeanPostProcessor組件,web服務(wù)工廠定制器的后置增強(qiáng)

導(dǎo)入了三種不同的web服務(wù)器(Tomcat箱叁、Jetty铡羡、Undertow)积蔚,默認(rèn)是Tomcat

我們來(lái)看一下 EmbeddedTomcat:

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {

   @Configuration(proxyBeanMethods = false)
   @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
   @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
   static class EmbeddedTomcat {

      @Bean
      TomcatServletWebServerFactory tomcatServletWebServerFactory(
            ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
            ObjectProvider<TomcatContextCustomizer> contextCustomizers,
            ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
         TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
         factory.getTomcatConnectorCustomizers()
               .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
         factory.getTomcatContextCustomizers()
               .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
         factory.getTomcatProtocolHandlerCustomizers()
               .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
         return factory;
      }

   }
}

tomcatServletWebServerFactory()方法中的參數(shù)都是從容器中獲取,我們可以自定義直接放到容器中烦周。實(shí)現(xiàn)ServletWebServerFactory 接口,該接口的getWebServlet()方法就是創(chuàng)建
Tomcat容器尽爆。

1.2 DispatcherServletAutoConfiguration

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

  
   public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

   public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

   @Configuration(proxyBeanMethods = false)
   @Conditional(DefaultDispatcherServletCondition.class)
   @ConditionalOnClass(ServletRegistration.class)
   @EnableConfigurationProperties(WebMvcProperties.class)
   protected static class DispatcherServletConfiguration {

      @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
      public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
         DispatcherServlet dispatcherServlet = new DispatcherServlet();
         dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
         dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
         dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
         dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
         dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
         return dispatcherServlet;
      }

      @Bean
      @ConditionalOnBean(MultipartResolver.class)
      @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
      public MultipartResolver multipartResolver(MultipartResolver resolver) {
         // Detect if the user has created a MultipartResolver but named it incorrectly
         return resolver;
      }

   }

   @Configuration(proxyBeanMethods = false)
   @Conditional(DispatcherServletRegistrationCondition.class)
   @ConditionalOnClass(ServletRegistration.class)
   @EnableConfigurationProperties(WebMvcProperties.class)
   @Import(DispatcherServletConfiguration.class)
   protected static class DispatcherServletRegistrationConfiguration {

      @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
      @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
      public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
            WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
         DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
               webMvcProperties.getServlet().getPath());
         registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
         registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
         multipartConfig.ifAvailable(registration::setMultipartConfig);
         return registration;
      }

   }
}
  • dispatcherServlet方法就是往容器中注入DispatcherServlet組件。
  • dispatcherServletRegistration() 方法就是將將DispatcherServlet組件添加到Tomcat中(tomcat.addServlet()

我們來(lái)看一下DispatcherServletRegistrationBean繼承圖:

image.png

在Tomcat啟動(dòng)的時(shí)候 會(huì)調(diào)用 ServletContextInitializer.onStartup() 方法回調(diào)读慎,將DispatcherServlet 添加到 Tomcat中教翩,后面的啟動(dòng)流程就跟SpringMVC的啟動(dòng)流程是一樣的

2. 核心方法run()

下面是一個(gè)簡(jiǎn)單的項(xiàng)目,我們直接開(kāi)始看run方法

@SpringBootApplication
public class SpringbootDemoApplication {
    
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(SpringbootDemoApplication.class, args);
        Object demoService = run.getBean("demoService");
        System.out.println("demoService = " + demoService);
   }
}

我們這里直接看 SpringApplication#run(java.lang.Class<?>[], java.lang.String[]) 方法:

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
}

我們這里先看一下 SpringApplication 的構(gòu)造函數(shù)流程:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 保存啟動(dòng)類信息
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 初始化環(huán)境贪壳。環(huán)境分為三種 非web環(huán)境饱亿、web環(huán)境、reactive環(huán)境三種闰靴。其判斷邏輯就是判斷是否存在指定的類彪笼,默認(rèn)是Servlet 環(huán)境,我們這也是Servlet
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // getSpringFactoriesInstances 方法加載了 spring.factories文件蚂且。在這里進(jìn)行了首次加載spring.factoies文件配猫。設(shè)置 ApplicationContextInitializer
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 獲取監(jiān)聽(tīng)器,也加載了spring.factories文件
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 設(shè)置啟動(dòng)類信息
        this.mainApplicationClass = deduceMainApplicationClass();
}

我們下面直接來(lái)看 SpringApplication#run(java.lang.String...) 方法的執(zhí)行流程:

public ConfigurableApplicationContext run(String... args) {
        // 開(kāi)啟關(guān)于啟動(dòng)時(shí)間的信息監(jiān)控
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 準(zhǔn)備 ApplicationContext
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        //java.awt.headless是J2SE的一種模式用于在缺少顯示屏杏死、鍵盤或者鼠標(biāo)時(shí)的系統(tǒng)配置泵肄,很多監(jiān)控工具如jconsole 需要將該值設(shè)置為true捆交,系統(tǒng)變量默認(rèn)為true
        configureHeadlessProperty();
        // 1. 獲取Spring的監(jiān)聽(tīng)器類,這里是從 spring.factories 中去獲取腐巢,默認(rèn)的是以 org.springframework.boot.SpringApplicationRunListener 為key,獲取到的監(jiān)聽(tīng)器類型為 EventPublishingRunListener品追。
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 1.1 監(jiān)聽(tīng)器發(fā)送啟動(dòng)事件
        listeners.starting();
        try {
                // 封裝參數(shù)
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                // 2. 構(gòu)造容器環(huán)境。將容器的一些配置內(nèi)容加載到 environment  中
                ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
                // 配置BeanInfo的忽略 :“spring.beaninfo.ignore”冯丙,值為“true”表示跳過(guò)對(duì)BeanInfo類的搜索
                configureIgnoreBeanInfo(environment);
                // 打印信息對(duì)象
                Banner printedBanner = printBanner(environment);
                // 3. 創(chuàng)建上下文對(duì)象
                context = createApplicationContext();
                // 從 spring.factries 中獲取錯(cuò)誤報(bào)告的類肉瓦。出錯(cuò)的時(shí)候會(huì)調(diào)用其方法通知
                exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                new Class[] { ConfigurableApplicationContext.class }, context);
                // 4. 準(zhǔn)備刷新上下文
                prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                // 5. 刷新上下文
                refreshContext(context);
                // 結(jié)束刷新,留待擴(kuò)展功能胃惜,并未實(shí)現(xiàn)什么
                afterRefresh(context, applicationArguments);
                // 停止監(jiān)聽(tīng)
                stopWatch.stop();
                if (this.logStartupInfo) {
                        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
                }
                // 監(jiān)聽(tīng)器發(fā)送啟動(dòng)結(jié)束時(shí)間
                listeners.started(context);
                // 調(diào)用 ApplicationRunner 和 CommandLineRunner 對(duì)應(yīng)的方法
                callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, listeners);
                throw new IllegalStateException(ex);
        }

        try {
                // 發(fā)送容器運(yùn)行事件
                listeners.running(context);
        }
        catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, null);
                throw new IllegalStateException(ex);
        }
        return context;
}

3. run()流程步驟詳解

下面我們重點(diǎn)分析幾個(gè)步驟

3.1 獲取監(jiān)聽(tīng)器

這一步是從 spring.factories 文件中獲取監(jiān)聽(tīng)器集合泞莉,當(dāng)有事件發(fā)生時(shí)調(diào)用監(jiān)聽(tīng)器對(duì)應(yīng)事件的方法。
默認(rèn)的是以 org.springframework.boot.SpringApplicationRunListener 為key,獲取到的監(jiān)聽(tīng)器類型為 EventPublishingRunListener船殉。

SpringApplicationRunListeners listeners = getRunListeners(args);

其詳細(xì)代碼如下:

private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
        return new SpringApplicationRunListeners(logger,
                        getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

這里需要注意的是 getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args) 返回的是一個(gè)Collection 類型鲫趁。也就是說(shuō)明在 SpringApplicationRunListeners并非代表一個(gè)監(jiān)聽(tīng)器,而是保存了監(jiān)聽(tīng)器集合利虫,在默認(rèn)情況下饮寞,僅有一個(gè) EventPublishingRunListener。在 SpringApplicationRunListeners 類中也能看到列吼,如下:

class SpringApplicationRunListeners {

    private final Log log;

    private final List<SpringApplicationRunListener> listeners;

    SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
        this.log = log;
        this.listeners = new ArrayList<>(listeners);
    }
    ...
}

總結(jié)一下: Spring啟動(dòng)時(shí)幽崩,通過(guò) spring.factories 文件中獲取監(jiān)聽(tīng)器集合。默認(rèn)類型為 EventPublishingRunListener寞钥。在事件發(fā)生時(shí)慌申,EventPublishingRunListener 會(huì)尋找容器中 ApplicationListener 的bean,并進(jìn)行事件通知理郑。詳見(jiàn)Spring5源碼12-監(jiān)聽(tīng)器原理

3.2 環(huán)境變量的構(gòu)造

這一步的作用就是加載一些配置文件的內(nèi)容

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

其具體實(shí)現(xiàn)如下:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
        // Create and configure the environment
        // 獲取或者創(chuàng)建 environment蹄溉。這里獲取類型是 StandardServletEnvironment 
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        // 將入?yún)⑴渲玫江h(huán)境配置中
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach(environment);
        // 發(fā)布環(huán)境準(zhǔn)備事件。
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
                environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                                deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
}

private Class<? extends StandardEnvironment> deduceEnvironmentClass() {
        switch (this.webApplicationType) {
        case SERVLET:
                return StandardServletEnvironment.class;
        case REACTIVE:
                return StandardReactiveWebEnvironment.class;
        default:
                return StandardEnvironment.class;
        }
}

關(guān)于 webApplicationType 的值您炉,在 org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...) 構(gòu)造函數(shù)中進(jìn)行了賦值為Servlet柒爵。所以我們這里可以知道 Environment 類型為 StandardServletEnvironment 。
在 listeners.environmentPrepared(environment); 時(shí)會(huì)發(fā)送環(huán)境準(zhǔn)備事件赚爵,環(huán)境準(zhǔn)備事件要通知監(jiān)聽(tīng)器如下棉胀。對(duì)于 Springboot 的配置文件application.yml或者application.properties文件的加載實(shí)際上是通過(guò)發(fā)布環(huán)境準(zhǔn)備事件完成的,完成這項(xiàng)功能的就是ConfigDataEnvironmentPostProcessor冀膝。

3.2.1 application.yml 的加載

SpringBoot在啟動(dòng)時(shí)唁奢,讀取所有的ApplicationListener下的配置類。spring-boot工程下的spring.factories文件:

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

當(dāng)EnvironmentPostProcessorApplicationListener類接收到ApplicationEnvironmentPreparedEvent的事件時(shí)窝剖,就會(huì)調(diào)用EnvironmentPostProcessorsFactory類下的getEnvironmentPostProcessors方法來(lái)獲取所有的EnvironmentPostProcessor下的配置類麻掸,并觸發(fā)這些類的postProcessEnvironment方法。

public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered {
    ...
   
        public EnvironmentPostProcessorApplicationListener() {
           this(EnvironmentPostProcessorsFactory::fromSpringFactories, new DeferredLogs());
        }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
        }
        ...
    }

    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        ...
        for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
                event.getBootstrapContext())) {
            postProcessor.postProcessEnvironment(environment, application);
        }
    }

    List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,
            ConfigurableBootstrapContext bootstrapContext) {
        ...
        EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
        return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
    }
}


@FunctionalInterface
public interface EnvironmentPostProcessorsFactory {

   List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory,
         ConfigurableBootstrapContext bootstrapContext);

   static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
      return new ReflectionEnvironmentPostProcessorsFactory(classLoader,
            SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));
   }

spring.factories文件中 :

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor,\
org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor

EnvironmentPostProcessor

EnvironmentPostProcessor是處理程序的配置的赐纱,不同的子類實(shí)現(xiàn)不同的配置脊奋,有處理json配置文件的熬北、有處理yaml配置文件的、有替換配置文件中的環(huán)境變量的诚隙。

SpringBoot 2.4.0之前的版本讶隐,處理yaml配置文件的類是ConfigFileApplicationListener類
SpringBoot 2.4.0之后的版本,處理yaml配置文件的類是ConfigDataEnvironmentPostProcessor類,我們重點(diǎn)看這個(gè)最楷。

ConfigDataEnvironmentPostProcessor

SpringBoot 2.4.0之后的版本,使用ConfigDataEnvironmentPostProcessor 類來(lái)加載yaml文件中的配置待错。

public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        postProcessEnvironment(environment, application.getResourceLoader(), application.getAdditionalProfiles());
    }

    void postProcessEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
            Collection<String> additionalProfiles) {
        ...
        getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).processAndApply();
        ...
    }

    ConfigDataEnvironment getConfigDataEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles) {
        return new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, environment, resourceLoader, additionalProfiles, this.environmentUpdateListener);
    }
}

ConfigDataEnvironment

ConfigDataEnvironmentPostProcessor 在postProcessEnvironment方法中會(huì)實(shí)例化一個(gè)ConfigDataEnvironment對(duì)象籽孙,這個(gè)對(duì)象會(huì)加載optional:classpath:/;optional:classpath:/config/;optional:file:./;optional:file:./config/;optional:file:./config/*/路徑下的yaml文件。

class ConfigDataEnvironment {
    static final String LOCATION_PROPERTY = "spring.config.location";
    static final String ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";
    static final String IMPORT_PROPERTY = "spring.config.import";
    
    static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS;
    static {
        List<ConfigDataLocation> locations = new ArrayList<>();
        locations.add(ConfigDataLocation.of("optional:classpath:/;optional:classpath:/config/"));
        locations.add(ConfigDataLocation.of("optional:file:./;optional:file:./config/;optional:file:./config/*/"));
        DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]);
    }

    private ConfigDataEnvironmentContributors createContributors(Binder binder) {
        ...
        contributors.addAll(getInitialImportContributors(binder));
        ...
        return createContributors(contributors);
    }

    protected ConfigDataEnvironmentContributors createContributors(
            List<ConfigDataEnvironmentContributor> contributors) {
        return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, contributors);
    }

    private List<ConfigDataEnvironmentContributor> getInitialImportContributors(Binder binder) {
        List<ConfigDataEnvironmentContributor> initialContributors = new ArrayList<>();
        addInitialImportContributors(initialContributors, bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS));
        addInitialImportContributors(initialContributors,
                bindLocations(binder, ADDITIONAL_LOCATION_PROPERTY, EMPTY_LOCATIONS));
        addInitialImportContributors(initialContributors,
                bindLocations(binder, LOCATION_PROPERTY, DEFAULT_SEARCH_LOCATIONS));
        return initialContributors;
    }
}

spring.factories文件中 :

# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
org.springframework.boot.context.config.StandardConfigDataLocationResolver

# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
org.springframework.boot.context.config.StandardConfigDataLoader

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

processAndApply()

請(qǐng)求路徑為:processAndApply() -> ConfigDataEnvironment#processAndApply -> ConfigDataEnvironment#processWithoutProfiles -> ConfigDataEnvironmentContributors#withProcessedImports -> ConfigDataImporter#resolveAndLoad -> ConfigDataImporter#load -> ConfigDataLoaders#load -> StandardConfigDataLoader#load -> YamlPropertySourceLoader#load
我們直接看YamlPropertySourceLoader#load 方法:

在這里加載 application.yml文件火俄。

EnvironmentPostProcessor

如果想處理加載的配置文件犯建,可以在自己的java項(xiàng)目里添加spring.factories,然后實(shí)現(xiàn)EnvironmentPostProcessor類瓜客。

org.springframework.boot.env.EnvironmentPostProcessor=\
com.camellibby.springboot.component.DemoEnvironmentPostProcessor
public class DemoEnvironmentPostProcessor implements EnvironmentPostProcessor {
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        System.out.println("DemoEnvironmentPostProcessor is starting");
    }
}

3.3 創(chuàng)建上下文

這一步是創(chuàng)建上下文了:

context = createApplicationContext();

其詳細(xì)內(nèi)容如下:

public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
                + "annotation.AnnotationConfigApplicationContext";

public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
                + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";

public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
                + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";


protected ConfigurableApplicationContext createApplicationContext() {
   return this.applicationContextFactory.create(this.webApplicationType);
}

// org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext.Factory#create
static class Factory implements ApplicationContextFactory {

   @Override
   public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
      return (webApplicationType != WebApplicationType.SERVLET) ? null
            : new AnnotationConfigServletWebServerApplicationContext();
   }

}

public AnnotationConfigServletWebServerApplicationContext() {
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

很明顯适瓦,因?yàn)槲覀冎?webApplicationType 值是 servlet,所以這里創(chuàng)建的是 AnnotationConfigServletWebServerApplicationContext 類型的上下文
這里需要注意:AnnotationConfigServletWebServerApplicationContext 構(gòu)造函數(shù)中會(huì)創(chuàng)建 AnnotatedBeanDefinitionReader谱仪。而在 AnnotatedBeanDefinitionReader 構(gòu)造函數(shù)中會(huì)調(diào)用 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);玻熙,該方法將一些必要Bean(如ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor疯攒、CommonAnnotationBeanPostProcessor 等)注入到了容器中嗦随。

3.4 上下文準(zhǔn)備工作

上面一步,僅僅是將上下文創(chuàng)建出來(lái)了敬尺,并沒(méi)有對(duì)上下文進(jìn)行操作枚尼。這一步開(kāi)始對(duì)上下文的準(zhǔn)備操作。

prepareContext(context, environment, listeners, applicationArguments, printedBanner);

其詳細(xì)內(nèi)容如下:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // 設(shè)置上下文的環(huán)境變量
        context.setEnvironment(environment);
        // 執(zhí)行容器后置處理 : 可以注冊(cè)beanName策略生成器砂吞、設(shè)置資源加載器署恍,設(shè)置轉(zhuǎn)換服務(wù)等。但這里默認(rèn)是沒(méi)有做任何處理蜻直。目的是留給后續(xù)可以擴(kuò)展
        postProcessApplicationContext(context);
        // 處理所有的初始化類的初始化方法盯质。即 spring.factories 中key 為 org.springframework.context.ApplicationContextInitializer 指向的類,調(diào)用其 initialize 方法
        applyInitializers(context);
        // 向監(jiān)聽(tīng)器發(fā)送容器準(zhǔn)備事件
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
                logStartupInfo(context.getParent() == null);
                logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        // 獲取上下文中的 BeanFactory概而。這里的BeanFactory 實(shí)際類型是  DefaultListableBeanFactory唤殴。BeanFactory 在初始化的時(shí)候,直接在構(gòu)造函數(shù)里創(chuàng)建為 DefaultListableBeanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 注冊(cè) springApplicationArguments等一系列bean
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
                beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
                // 設(shè)置是否允許bean定義覆蓋
                ((DefaultListableBeanFactory) beanFactory)
                                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        // 如果允許懶加載到腥,則添加對(duì)應(yīng)的BeanFactory后置處理器
        if (this.lazyInitialization) {
                context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the sources
        // 這里加載的實(shí)際上是啟動(dòng)類
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // 這里將啟動(dòng)類加入到 beanDefinitionMap 中朵逝,為后續(xù)的自動(dòng)化配置做好了基礎(chǔ)
        load(context, sources.toArray(new Object[0]));
        // 發(fā)送容器加載完成事件
        listeners.contextLoaded(context);
}

....

// 需要注意這里的 sources參數(shù)實(shí)際上是 啟動(dòng)類的 Class
protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
                logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        // 從上下文中獲取 BeanDefinitionRegistry并依次創(chuàng)建出 BeanDefinitionLoader 。這里將sources作為參數(shù)保存到了 loader  中乡范。也就是 loader  中保存了 啟動(dòng)類的Class信息
        BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
                loader.setBeanNameGenerator(this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
                loader.setResourceLoader(this.resourceLoader);
        }
        if (this.environment != null) {
                loader.setEnvironment(this.environment);
        }
        loader.load();
}

我們這里比較關(guān)鍵的方法是 loader.load(); 方法 其中 loader.load(); 不管怎么跳轉(zhuǎn)配名,最后都會(huì)跳轉(zhuǎn)到 BeanDefinitionLoader#load(java.lang.Class<?>) 方法中啤咽。如下:

private int load(Class<?> source) {
        // 判斷是否存在 groovy 加載方式
        if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
                // Any GroovyLoaders added in beans{} DSL can contribute beans here
                GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
                load(loader);
        }
        // 判斷 source 是否 需要被加載到Spring容器中。實(shí)際上是根據(jù)判斷是否存在 @Component 
        if (isComponent(source)) {
                // 將source 就是啟動(dòng)類的 class渠脉,注冊(cè)到 annotatedReader 中宇整。annotatedReader  類型是AnnotatedBeanDefinitionReader。
                this.annotatedReader.register(source);
                return 1;
        }
        return 0;
}

this.annotatedReader.register(source); 后續(xù)會(huì)跳轉(zhuǎn)到 AnnotatedBeanDefinitionReader#doRegisterBean 方法中芋膘,看名字就知道是這個(gè)方法的工作是 注冊(cè) Bean鳞青。實(shí)際上,在這個(gè)方法中完成了對(duì)@Qualifier 以及一些其他注解的處理为朋。具體如下:

// 這里的 beanClass 其實(shí)就是啟動(dòng)類的 beanClass 
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
                @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
                @Nullable BeanDefinitionCustomizer[] customizers) {
        // 將Class 轉(zhuǎn)換成一個(gè) BeanDefinition 類
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
        // 判斷是否應(yīng)該跳過(guò)
        if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
                return;
        }

        abd.setInstanceSupplier(supplier);
        // 保存其作用域信息臂拓。這里默認(rèn)是 singleton
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        abd.setScope(scopeMetadata.getScopeName());
        // 獲取 beanName
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
        // 處理一些通用的注解信息,包括Lazy习寸、Primary胶惰、DependsOn、Role霞溪、Description 注解孵滞。獲取其value值并保存到 abd 中
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
        // 處理  @Qualifier 
        if (qualifiers != null) {
                for (Class<? extends Annotation> qualifier : qualifiers) {
                        if (Primary.class == qualifier) {
                                abd.setPrimary(true);
                        }
                        else if (Lazy.class == qualifier) {
                                abd.setLazyInit(true);
                        }
                        else {
                                abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                        }
                }
        }
        if (customizers != null) {
                for (BeanDefinitionCustomizer customizer : customizers) {
                        customizer.customize(abd);
                }
        }

        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
        // 判斷是否需要?jiǎng)?chuàng)建代理,需要?jiǎng)t創(chuàng)建
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        // 將 BeanDefinitionHolder  注冊(cè)到 容器中鸯匹。此時(shí)的 registry 就是 AnnotationConfigServletWebServerApplicationContext坊饶。在BeanDefinitionLoader 初始化的時(shí)候保存的
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

3.5 SpringApplication#refreshContext

對(duì)容器進(jìn)行一個(gè)刷新工作。在此進(jìn)行了大量的工作殴蓬。這里的處理工作就由Springboot交給 Spring來(lái)處理了

refreshContext(context);

詳細(xì)如下:

private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
        if (this.registerShutdownHook) {
                try {
                        context.registerShutdownHook();
                }
                catch (AccessControlException ex) {
                        // Not allowed in some environments.
                }
        }
}

最終會(huì)跳轉(zhuǎn)到 AbstractApplicationContext#refresh 中幼东。而關(guān)于 AbstractApplicationContext#refresh 方法在之前的文章中有過(guò)介紹,參見(jiàn)Spring5源碼11-容器刷新refresh方法(注解版)科雳。我們重點(diǎn)看一下Tomcat的啟動(dòng)過(guò)程根蟹,著重看onRefresh()方法:

@Override
public void refresh() throws BeansException, IllegalStateException {

         // Initialize other special beans in specific context subclasses.
         onRefresh();

         // Check for listener beans and register them.
         registerListeners();      
}

SpringBoot使用ServletWebServerApplicationContext#onRefresh:

@Override
protected void onRefresh() {
   super.onRefresh();
   try {
      createWebServer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
   }
}

直接看createWebServer()方法:

private void createWebServer() {
   WebServer webServer = this.webServer;
   ServletContext servletContext = getServletContext();
   if (webServer == null && servletContext == null) {
      StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
      // 本文環(huán)境獲取的是tomcatServletWebServerFactory,默認(rèn)的
      ServletWebServerFactory factory = getWebServerFactory();
      createWebServer.tag("factory", factory.getClass().toString());
      this.webServer = factory.getWebServer(getSelfInitializer());
      createWebServer.end();
      getBeanFactory().registerSingleton("webServerGracefulShutdown",
            new WebServerGracefulShutdownLifecycle(this.webServer));
      getBeanFactory().registerSingleton("webServerStartStop",
            new WebServerStartStopLifecycle(this, this.webServer));
   }
   else if (servletContext != null) {
      try {
         getSelfInitializer().onStartup(servletContext);
      }
      catch (ServletException ex) {
         throw new ApplicationContextException("Cannot initialize servlet context", ex);
      }
   }
   initPropertySources();
}

3.5.1 getWebServerFactory()

如下所示糟秘,從容器中獲取ServletWebServerFactory類型的bean简逮,唯一一個(gè),否則拋出異常尿赚。本文環(huán)境獲取的是tomcatServletWebServerFactory散庶。

protected ServletWebServerFactory getWebServerFactory() {
   // Use bean names so that we don't consider the hierarchy
   String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
   if (beanNames.length == 0) {
      throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
            + "ServletWebServerFactory bean.");
   }
   if (beanNames.length > 1) {
      throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
            + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
   }
   return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

3.5.2 getSelfInitializer()

ServletWebServerApplicationContext的getSelfInitializer方法,返回的是ServletContextInitializer凌净。

private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    return this::selfInitialize;
}

看到this::selfInitialize是不是比較迷糊悲龟?典型的java8的lambda寫法。我們看一下ServletContextInitializer 可能就明白了冰寻。
如下所示须教,其是一個(gè)函數(shù)式接口,只有一個(gè)onStartup方法。函數(shù)式接口(有且僅有一個(gè)抽象方法的接口)可以使用lambda式的寫法轻腺。

@FunctionalInterface
public interface ServletContextInitializer {
     // 初始化過(guò)程中乐疆,使用給定的servlets、filters贬养、listeners
     //context-params and attributes necessary配置ServletContext
    void onStartup(ServletContext servletContext) throws ServletException;

}

我們這里獲取到的本質(zhì)是一個(gè)lambda挤土,參數(shù)則是當(dāng)前this,如下圖所示:

image.png

this::selfInitialize中的selfInitialize則指的是ServletWebServerApplicationContext的selfInitialize方法误算。this指的是AnnotationConfigServletWebServerApplicationContext仰美,其繼承于ServletWebServerApplicationContext

private void selfInitialize(ServletContext servletContext) throws ServletException {
    prepareWebApplicationContext(servletContext);
    registerApplicationScope(servletContext);
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
}

其實(shí)換成匿名類的寫法則是:

new ServletContextInitializer() {
      @Override
      public void onStartup(ServletContext servletContext) throws ServletException {
        selfInitialize(servletContext);
      }
};

3.5.3 getWebServer()

本文這里是TomcatServletWebServerFactory#getWebServer方法。

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    if (this.disableMBeanRegistry) {
    // registry = new NoDescriptorRegistry();
        Registry.disableRegistry();
    }
    //實(shí)例化Tomcat
    Tomcat tomcat = new Tomcat();

    //獲取臨時(shí)路徑 
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    //設(shè)置基礎(chǔ)路徑
    tomcat.setBaseDir(baseDir.getAbsolutePath());

    //實(shí)例化Connector 并進(jìn)行配置
    Connector connector = new Connector(this.protocol);
    connector.setThrowOnFailure(true);

    //這里會(huì)實(shí)例化server  service
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);

    //對(duì)Connector做配置比如Protocol儿礼、URIEncoding
    tomcat.setConnector(connector);

    //這里會(huì)實(shí)例化Engine咖杂、Host
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
                // todo
        tomcat.getService().addConnector(additionalConnector);
    }
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
}

getService

getService首先會(huì)觸發(fā)getServer然后獲取service。getServer如下所示會(huì)實(shí)例化Server并對(duì)其進(jìn)行配置蜘犁。

public Service getService() {
    return getServer().findServices()[0];
}
public Server getServer() {
   if (server != null) {
       return server;
   }
   System.setProperty("catalina.useNaming", "false");
    // 實(shí)例化 server
   server = new StandardServer();

    // 對(duì)basedir做處理
   initBaseDir();

   // Set configuration source
   ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(basedir), null));
   // 為server設(shè)置port和service
   server.setPort( -1 );
   //實(shí)例化service
   Service service = new StandardService();
   service.setName("Tomcat");
   server.addService(service);
   return server;
}

prepareContext

這里會(huì)實(shí)例化TomcatEmbeddedContext并對(duì)其進(jìn)行配置翰苫。

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
    File documentRoot = getValidDocumentRoot();
    TomcatEmbeddedContext context = new TomcatEmbeddedContext();
    if (documentRoot != null) {
        context.setResources(new LoaderHidingResourceRoot(context));
    }
    context.setName(getContextPath());
    context.setDisplayName(getDisplayName());
    context.setPath(getContextPath());
    File docBase = (documentRoot != null) ? documentRoot : createTempDir("tomcat-docbase");
    context.setDocBase(docBase.getAbsolutePath());
    context.addLifecycleListener(new FixContextListener());
    context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
            : ClassUtils.getDefaultClassLoader());
    resetDefaultLocaleMapping(context);
    addLocaleMappings(context);
    context.setUseRelativeRedirects(false);
    try {
        context.setCreateUploadTargets(true);
    }
    catch (NoSuchMethodError ex) {
        // Tomcat is < 8.5.39. Continue.
    }
    configureTldSkipPatterns(context);
    WebappLoader loader = new WebappLoader(context.getParentClassLoader());
    loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
    loader.setDelegate(true);
    context.setLoader(loader);
    if (isRegisterDefaultServlet()) {
        addDefaultServlet(context);
    }
    if (shouldRegisterJspServlet()) {
        addJspServlet(context);
        addJasperInitializer(context);
    }
    context.addLifecycleListener(new StaticResourceConfigurer(context));
    ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
    host.addChild(context);
    configureContext(context, initializersToUse);
    postProcessContext(context);
}

getTomcatWebServer

這個(gè)方法很簡(jiǎn)單止邮,只是直接實(shí)例化了TomcatWebServer返回这橙。其構(gòu)造方法觸發(fā)了initialize,這會(huì)引起后續(xù)一系列動(dòng)作导披,包括tomcat.start()屈扎。

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    return new TomcatWebServer(tomcat, getPort() >= 0);
}
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
        // todo
    initialize();
}

initialize()

我們回過(guò)頭來(lái),看一下initialize()方法:

private void initialize() throws WebServerException {
   logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
   synchronized (this.monitor) {
      try {
         ...
         // Start the server to trigger initialization listeners
         this.tomcat.start();

        ...
      }
   }
}

在這里將嵌入式Tomcat啟動(dòng)撩匕。

selfInitialize

獲取到TomcatWebServer后鹰晨,Tomcat啟動(dòng)之后就觸發(fā)了selfInitialize方法。這里servletContext其實(shí)是獲取了ApplicationContext的一個(gè)門面/外觀–ApplicationContextCade止毕。

// ServletWebServerApplicationContext
private void selfInitialize(ServletContext servletContext) throws ServletException {
        // todo
    prepareWebApplicationContext(servletContext);
        // todo
    registerApplicationScope(servletContext);
        // todo
    WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
        // todo
    for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
    }
}

prepareWebApplicationContext
ServletWebServerApplicationContext的prepareWebApplicationContext方法如下所示模蜡,簡(jiǎn)單來(lái)講就是為servletContext設(shè)置根容器屬性并為當(dāng)前應(yīng)用上下文ApplicationContext設(shè)置servletContext引用。

protected void prepareWebApplicationContext(ServletContext servletContext) {
//嘗試從servletContext中獲取rootContext 
        Object rootContext = servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
        if (rootContext != null) {
                if (rootContext == this) {
                        throw new IllegalStateException(
                                        "Cannot initialize context because there is already a root application context present - "
                                                        + "check whether you have multiple ServletContextInitializers!");
                }
                return;
        }
        Log logger = LogFactory.getLog(ContextLoader.class);
        // 這個(gè)日志是不是很熟悉扁凛?忍疾!
        servletContext.log("Initializing Spring embedded WebApplicationContext");
        try {

//向servletContext設(shè)置屬性 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);
                if (logger.isDebugEnabled()) {
                        logger.debug("Published root WebApplicationContext as ServletContext attribute with name ["
                                        + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
                }
                // 為ApplicationContext設(shè)置servletContext引用
                setServletContext(servletContext);
                if (logger.isInfoEnabled()) {
                        long elapsedTime = System.currentTimeMillis() - getStartupDate();
                        logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
                }
        }
        catch (RuntimeException | Error ex) {
                logger.error("Context initialization failed", ex);
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
                throw ex;
        }
}

registerApplicationScope:
ServletWebServerApplicationContext的registerApplicationScope方法如下所示,簡(jiǎn)單來(lái)講就是(擴(kuò)展)注冊(cè)scope-application谨朝。這里會(huì)實(shí)例化一個(gè)ServletContextScope (包裝了servletContext)卤妒,然后注冊(cè)到BeanFactory中并為servletContext設(shè)置屬性。

private void registerApplicationScope(ServletContext servletContext) {
        ServletContextScope appScope = new ServletContextScope(servletContext);
        // application
        getBeanFactory().registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
        // Register as ServletContext attribute, for ContextCleanupListener to detect it.
        servletContext.setAttribute(ServletContextScope.class.getName(), appScope);
}

我們?cè)赟pring中refresh分析之postProcessBeanFactory方法詳解提到了request-RequestScope字币,session–SessionScope的注冊(cè)则披,本文這里注冊(cè)了application-ServletContextScope注冊(cè)。

registerEnvironmentBeans:
WebApplicationContextUtils的registerEnvironmentBeans方法洗出。

public static void registerEnvironmentBeans(ConfigurableListableBeanFactory bf, @Nullable ServletContext sc) {
        registerEnvironmentBeans(bf, sc, null);
}

public static void registerEnvironmentBeans(ConfigurableListableBeanFactory bf,
                @Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {

//將servletContext作為單例注冊(cè)容器
        if (servletContext != null && !bf.containsBean(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME)) {
                bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext);
        }

// 將servletConfig 作為單例注冊(cè)容器本文這里沒(méi)有觸發(fā)
        if (servletConfig != null && !bf.containsBean(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME)) {
                bf.registerSingleton(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME, servletConfig);
        }
// String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
        if (!bf.containsBean(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME)) {
                Map<String, String> parameterMap = new HashMap<>();
                if (servletContext != null) {
                // 獲取servletContextd的初始化參數(shù)
                        Enumeration<?> paramNameEnum = servletContext.getInitParameterNames();
                        while (paramNameEnum.hasMoreElements()) {
                                String paramName = (String) paramNameEnum.nextElement();
                                parameterMap.put(paramName, servletContext.getInitParameter(paramName));
                        }
                }
                // 本文這里servletConfig 為null
                if (servletConfig != null) {
                // // 獲取servletConfig的初始化參數(shù)
                        Enumeration<?> paramNameEnum = servletConfig.getInitParameterNames();
                        while (paramNameEnum.hasMoreElements()) {
                                String paramName = (String) paramNameEnum.nextElement();
                                parameterMap.put(paramName, servletConfig.getInitParameter(paramName));
                        }
                }
                // 將contextParameters作為單例注冊(cè)到容器
                bf.registerSingleton(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME,
                                Collections.unmodifiableMap(parameterMap));
        }

// String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
        if (!bf.containsBean(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME)) {
                Map<String, Object> attributeMap = new HashMap<>();
                if (servletContext != null) {
                        Enumeration<?> attrNameEnum = servletContext.getAttributeNames();
                        while (attrNameEnum.hasMoreElements()) {
                                String attrName = (String) attrNameEnum.nextElement();
                                attributeMap.put(attrName, servletContext.getAttribute(attrName));
                        }
                }
                // 將contextAttributes作為單例注冊(cè)到容器
                bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME,
                                Collections.unmodifiableMap(attributeMap));
        }
}

觸發(fā)ServletContextInitializer的onStartup:如下所示士复,這里會(huì)獲取ServletContextInitializer的所有實(shí)例,遍歷觸發(fā)其onStartup方法翩活。

for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
        beans.onStartup(servletContext);
}

如下所示判没,將會(huì)挨個(gè)觸發(fā)這5個(gè)的onStartup方法:


image.png

核心代碼如下:

// org.springframework.boot.web.servlet.RegistrationBean#onStartup
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
   String description = getDescription();
   if (!isEnabled()) {
      logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
      return;
   }
   register(description, servletContext);
}
// org.springframework.boot.web.servlet.DynamicRegistrationBean#register
@Override
protected final void register(String description, ServletContext servletContext) {
   D registration = addRegistration(description, servletContext);
   if (registration == null) {
      logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
      return;
   }
   configure(registration);
}
// org.springframework.boot.web.servlet.ServletRegistrationBean#addRegistration
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
   String name = getServletName();
   return servletContext.addServlet(name, this.servlet);
}

在這里蜓萄,會(huì)將Dispatcher加入到Tomcat中,之后就是Tomcat的啟動(dòng)流程澄峰,跟之前啟動(dòng)SpringMVC流程一樣

3.6 afterRefresh

進(jìn)入org.springframework.boot.SpringApplication#afterRefresh方法中

protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}

這里啥也沒(méi)做嫉沽,就是一個(gè)模板方法,可能使用于擴(kuò)展性的吧

3.7 callRunners

進(jìn)入org.springframework.boot.SpringApplication#callRunners方法中

private void callRunners(ApplicationContext context, ApplicationArguments args) {
   List<Object> runners = new ArrayList<>();
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   AnnotationAwareOrderComparator.sort(runners);
   for (Object runner : new LinkedHashSet<>(runners)) {
      if (runner instanceof ApplicationRunner) {
         callRunner((ApplicationRunner) runner, args);
      }
      if (runner instanceof CommandLineRunner) {
         callRunner((CommandLineRunner) runner, args);
      }
   }
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
   try {
      (runner).run(args);
   }
   catch (Exception ex) {
      throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
   }
}

可以看到這里主要就是從容器中獲取runner對(duì)象并調(diào)用對(duì)應(yīng)的run方法俏竞。

3.8 流程框圖

image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绸硕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌霉猛,老刑警劉巖笔链,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異咬崔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)烦秩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門垮斯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人只祠,你說(shuō)我怎么就攤上這事兜蠕。” “怎么了抛寝?”我有些...
    開(kāi)封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵熊杨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我盗舰,道長(zhǎng)晶府,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任钻趋,我火速辦了婚禮川陆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘爷绘。我一直安慰自己书劝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布土至。 她就那樣靜靜地躺著购对,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陶因。 梳的紋絲不亂的頭發(fā)上骡苞,一...
    開(kāi)封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼解幽。 笑死贴见,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的躲株。 我是一名探鬼主播片部,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼霜定!你這毒婦竟也來(lái)了档悠?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤望浩,失蹤者是張志新(化名)和其女友劉穎辖所,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磨德,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缘回,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了典挑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酥宴。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖搔弄,靈堂內(nèi)的尸體忽然破棺而出幅虑,到底是詐尸還是另有隱情丰滑,我是刑警寧澤顾犹,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站褒墨,受9級(jí)特大地震影響炫刷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜郁妈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一浑玛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧噩咪,春花似錦顾彰、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至仆百,卻和暖如春厕隧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工吁讨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留髓迎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓建丧,卻偏偏與公主長(zhǎng)得像排龄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子翎朱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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