SpringBoot2源码2-核心启动过程和run方法

 2023-01-08
原文作者:hsfxuebao 原文地址:https://juejin.cn/post/7145642930584174605

欢迎大家关注 github.com/hsfxuebao ,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈

1. SpringBoot中怎么启动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 导入WebServerFactoryCustomizerBeanPostProcessor组件,web服务工厂定制器的后置增强
  • 导入了三种不同的web服务器(Tomcat、Jetty、Undertow),默认是Tomcat

我们来看一下 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()方法中的参数都是从容器中获取,我们可以自定义直接放到容器中。实现ServletWebServerFactory接口,该接口的getWebServlet()方法就是创建 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()

我们来看一下DispatcherServletRegistrationBean继承图:

202301012004216191.png

在Tomcat启动的时候 会调用 ServletContextInitializer.onStartup() 方法回调,将DispatcherServlet 添加到 Tomcat中,后面的启动流程就跟SpringMVC的启动流程是一样的,参见Spring5源码14-SpringMVC入口及启动流程

2. 核心方法run()

下面是一个简单的项目,我们直接开始看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 的构造函数流程:

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
            this.resourceLoader = resourceLoader;
            Assert.notNull(primarySources, "PrimarySources must not be null");
            // 保存启动类信息
            this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
            // 初始化环境。环境分为三种 非web环境、web环境、reactive环境三种。其判断逻辑就是判断是否存在指定的类,默认是Servlet 环境,我们这也是Servlet
            this.webApplicationType = WebApplicationType.deduceFromClasspath();
            // getSpringFactoriesInstances 方法加载了 spring.factories文件。在这里进行了首次加载spring.factoies文件。设置 ApplicationContextInitializer
            setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
            // 获取监听器,也加载了spring.factories文件
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            // 设置启动类信息
            this.mainApplicationClass = deduceMainApplicationClass();
    }

我们下面直接来看 SpringApplication#run(java.lang.String...) 方法的执行流程:

    public ConfigurableApplicationContext run(String... args) {
            // 开启关于启动时间的信息监控
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            // 准备 ApplicationContext
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            //java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
            configureHeadlessProperty();
            // 1. 获取Spring的监听器类,这里是从 spring.factories 中去获取,默认的是以 org.springframework.boot.SpringApplicationRunListener 为key,获取到的监听器类型为 EventPublishingRunListener。
            SpringApplicationRunListeners listeners = getRunListeners(args);
            // 1.1 监听器发送启动事件
            listeners.starting();
            try {
                    // 封装参数
                    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                    // 2. 构造容器环境。将容器的一些配置内容加载到 environment  中
                    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
                    // 配置BeanInfo的忽略 :“spring.beaninfo.ignore”,值为“true”表示跳过对BeanInfo类的搜索
                    configureIgnoreBeanInfo(environment);
                    // 打印信息对象
                    Banner printedBanner = printBanner(environment);
                    // 3. 创建上下文对象
                    context = createApplicationContext();
                    // 从 spring.factries 中获取错误报告的类。出错的时候会调用其方法通知
                    exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                    new Class[] { ConfigurableApplicationContext.class }, context);
                    // 4. 准备刷新上下文
                    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                    // 5. 刷新上下文
                    refreshContext(context);
                    // 结束刷新,留待扩展功能,并未实现什么
                    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;
    }

3. run()流程步骤详解

下面我们重点分析几个步骤

3.1 获取监听器

这一步是从 spring.factories 文件中获取监听器集合,当有事件发生时调用监听器对应事件的方法。

默认的是以org.springframework.boot.SpringApplicationRunListener 为key,获取到的监听器类型为 EventPublishingRunListener

    SpringApplicationRunListeners listeners = getRunListeners(args);

其详细代码如下:

    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)返回的是一个Collection 类型。也就是说明在 SpringApplicationRunListeners并非代表一个监听器,而是保存了监听器集合,在默认情况下,仅有一个 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);
    	}
    	...
    }

总结一下: Spring启动时,通过 spring.factories 文件中获取监听器集合。默认类型为 EventPublishingRunListener。在事件发生时,EventPublishingRunListener 会寻找容器中 ApplicationListener 的bean,并进行事件通知。详见Spring5源码12-监听器原理

3.2 环境变量的构造

这一步的作用就是加载一些配置文件的内容

    ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

其具体实现如下:

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                    ApplicationArguments applicationArguments) {
            // Create and configure the environment
            // 获取或者创建 environment。这里获取类型是 StandardServletEnvironment 
            ConfigurableEnvironment environment = getOrCreateEnvironment();
            // 将入参配置到环境配置中
            configureEnvironment(environment, applicationArguments.getSourceArgs());
            ConfigurationPropertySources.attach(environment);
            // 发布环境准备事件。
            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;
            }
    }

关于 webApplicationType 的值,在 org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...) 构造函数中进行了赋值为Servlet。所以我们这里可以知道 Environment 类型为 StandardServletEnvironment

listeners.environmentPrepared(environment); 时会发送环境准备事件,环境准备事件要通知监听器如下。对于 Springboot 的配置文件application.yml或者application.properties文件的加载实际上是通过发布环境准备事件完成的,完成这项功能的就是ConfigDataEnvironmentPostProcessor

3.2.1 application.yml 的加载

SpringBoot在启动时,读取所有的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

EnvironmentPostProcessorApplicationListener类接收到ApplicationEnvironmentPreparedEvent的事件时,就会调用EnvironmentPostProcessorsFactory类下的getEnvironmentPostProcessors方法来获取所有的EnvironmentPostProcessor下的配置类,并触发这些类的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是处理程序的配置的,不同的子类实现不同的配置,有处理json配置文件的、有处理yaml配置文件的、有替换配置文件中的环境变量的。

  • SpringBoot 2.4.0之前的版本,处理yaml配置文件的类是ConfigFileApplicationListener类
  • SpringBoot 2.4.0之后的版本,处理yaml配置文件的类是ConfigDataEnvironmentPostProcessor类,我们重点看这个。

ConfigDataEnvironmentPostProcessor

SpringBoot 2.4.0之后的版本,使用ConfigDataEnvironmentPostProcessor 类来加载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

ConfigDataEnvironmentPostProcessorpostProcessEnvironment方法中会实例化一个ConfigDataEnvironment对象,这个对象会加载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()

请求路径为:processAndApply() -> ConfigDataEnvironment#processAndApply -> ConfigDataEnvironment#processWithoutProfiles -> ConfigDataEnvironmentContributors#withProcessedImports -> ConfigDataImporter#resolveAndLoad -> ConfigDataImporter#load -> ConfigDataLoaders#load -> StandardConfigDataLoader#load -> YamlPropertySourceLoader#load

我们直接看YamlPropertySourceLoader#load 方法:

    public class YamlPropertySourceLoader implements PropertySourceLoader {
    
    	@Override
    	public String[] getFileExtensions() {
    		return new String[] { "yml", "yaml" };
    	}
    
    	@Override
    	public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
    		if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", getClass().getClassLoader())) {
    			throw new IllegalStateException(
    					"Attempted to load " + name + " but snakeyaml was not found on the classpath");
    		}
    		List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
    		if (loaded.isEmpty()) {
    			return Collections.emptyList();
    		}
    		List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
    		for (int i = 0; i < loaded.size(); i++) {
    			String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : "";
    			propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
    					Collections.unmodifiableMap(loaded.get(i)), true));
    		}
    		return propertySources;
    	}
    
    }

在这里加载 application.yml文件。

EnvironmentPostProcessor

如果想处理加载的配置文件,可以在自己的java项目里添加spring.factories,然后实现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 创建上下文

这一步是创建上下文了:

    context = createApplicationContext();

其详细内容如下:

    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);
    }

很明显,因为我们知道 webApplicationType 值是 servlet,所以这里创建的是 AnnotationConfigServletWebServerApplicationContext 类型的上下文

这里需要注意:AnnotationConfigServletWebServerApplicationContext 构造函数中会创建 AnnotatedBeanDefinitionReader。而在 AnnotatedBeanDefinitionReader 构造函数中会调用 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);,该方法将一些必要Bean(如ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor 等)注入到了容器中。

3.4 上下文准备工作

上面一步,仅仅是将上下文创建出来了,并没有对上下文进行操作。这一步开始对上下文的准备操作。

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

其详细内容如下:

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                    SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
            // 设置上下文的环境变量
            context.setEnvironment(environment);
            // 执行容器后置处理 : 可以注册beanName策略生成器、设置资源加载器,设置转换服务等。但这里默认是没有做任何处理。目的是留给后续可以扩展
            postProcessApplicationContext(context);
            // 处理所有的初始化类的初始化方法。即 spring.factories 中key 为 org.springframework.context.ApplicationContextInitializer 指向的类,调用其 initialize 方法
            applyInitializers(context);
            // 向监听器发送容器准备事件
            listeners.contextPrepared(context);
            if (this.logStartupInfo) {
                    logStartupInfo(context.getParent() == null);
                    logStartupProfileInfo(context);
            }
            // Add boot specific singleton beans
            // 获取上下文中的 BeanFactory。这里的BeanFactory 实际类型是  DefaultListableBeanFactory。BeanFactory 在初始化的时候,直接在构造函数里创建为 DefaultListableBeanFactory
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            // 注册 springApplicationArguments等一系列bean
            beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
            if (printedBanner != null) {
                    beanFactory.registerSingleton("springBootBanner", printedBanner);
            }
            if (beanFactory instanceof DefaultListableBeanFactory) {
                    // 设置是否允许bean定义覆盖
                    ((DefaultListableBeanFactory) beanFactory)
                                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
            // 如果允许懒加载,则添加对应的BeanFactory后置处理器
            if (this.lazyInitialization) {
                    context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
            }
            // Load the sources
            // 这里加载的实际上是启动类
            Set<Object> sources = getAllSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            // 这里将启动类加入到 beanDefinitionMap 中,为后续的自动化配置做好了基础
            load(context, sources.toArray(new Object[0]));
            // 发送容器加载完成事件
            listeners.contextLoaded(context);
    }
    
    ....
    
    // 需要注意这里的 sources参数实际上是 启动类的 Class
    protected void load(ApplicationContext context, Object[] sources) {
            if (logger.isDebugEnabled()) {
                    logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
            }
            // 从上下文中获取 BeanDefinitionRegistry并依次创建出 BeanDefinitionLoader 。这里将sources作为参数保存到了 loader  中。也就是 loader  中保存了 启动类的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();
    }

我们这里比较关键的方法是 loader.load(); 方法 其中 loader.load(); 不管怎么跳转,最后都会跳转到 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容器中。实际上是根据判断是否存在 @Component 
            if (isComponent(source)) {
                    // 将source 就是启动类的 class,注册到 annotatedReader 中。annotatedReader  类型是AnnotatedBeanDefinitionReader。
                    this.annotatedReader.register(source);
                    return 1;
            }
            return 0;
    }

this.annotatedReader.register(source); 后续会跳转到 AnnotatedBeanDefinitionReader#doRegisterBean 方法中,看名字就知道是这个方法的工作是注册 Bean。实际上,在这个方法中完成了对@Qualifier 以及一些其他注解的处理。具体如下:

    // 这里的 beanClass 其实就是启动类的 beanClass 
    private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
                    @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
                    @Nullable BeanDefinitionCustomizer[] customizers) {
            // 将Class 转换成一个 BeanDefinition 类
            AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
            // 判断是否应该跳过
            if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
                    return;
            }
    
            abd.setInstanceSupplier(supplier);
            // 保存其作用域信息。这里默认是 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);
            // 判断是否需要创建代理,需要则创建
            definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            // 将 BeanDefinitionHolder  注册到 容器中。此时的 registry 就是 AnnotationConfigServletWebServerApplicationContext。在BeanDefinitionLoader 初始化的时候保存的
            BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }

3.5 SpringApplication#refreshContext

对容器进行一个刷新工作。在此进行了大量的工作。这里的处理工作就由Springboot交给 Spring来处理了

    refreshContext(context);

详细如下:

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

最终会跳转到 AbstractApplicationContext#refresh 中。而关于 AbstractApplicationContext#refresh 方法在之前的文章中有过介绍,参见Spring5源码11-容器刷新refresh方法(注解版)。我们重点看一下Tomcat的启动过程,着重看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");
          // 本文环境获取的是tomcatServletWebServerFactory,默认的
          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,唯一一个,否则抛出异常。本文环境获取的是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 可能就明白了。

如下所示,其是一个函数式接口,只有一个onStartup方法。函数式接口(有且仅有一个抽象方法的接口)可以使用lambda式的写法。

    @FunctionalInterface
    public interface ServletContextInitializer {
         // 初始化过程中,使用给定的servlets、filters、listeners
         //context-params and attributes necessary配置ServletContext
        void onStartup(ServletContext servletContext) throws ServletException;
    
    }

我们这里获取到的本质是一个lambda,参数则是当前this,如下图所示:

202301012004220942.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);
    	}
    }

其实换成匿名类的写法则是:

    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();
    	}
    	//实例化Tomcat
    	Tomcat tomcat = new Tomcat();
    
    	//获取临时路径 
    	File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    	//设置基础路径
    	tomcat.setBaseDir(baseDir.getAbsolutePath());
    
    	//实例化Connector 并进行配置
    	Connector connector = new Connector(this.protocol);
    	connector.setThrowOnFailure(true);
    
    	//这里会实例化server  service
    	tomcat.getService().addConnector(connector);
    	customizeConnector(connector);
    
    	//对Connector做配置比如Protocol、URIEncoding
    	tomcat.setConnector(connector);
    
    	//这里会实例化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首先会触发getServer然后获取service。getServer如下所示会实例化Server并对其进行配置。

    public Service getService() {
        return getServer().findServices()[0];
    }
    public Server getServer() {
       if (server != null) {
           return server;
       }
       System.setProperty("catalina.useNaming", "false");
        // 实例化 server
       server = new StandardServer();
    
        // 对basedir做处理
       initBaseDir();
    
       // Set configuration source
       ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(basedir), null));
       // 为server设置port和service
       server.setPort( -1 );
       //实例化service
       Service service = new StandardService();
       service.setName("Tomcat");
       server.addService(service);
       return server;
    }

prepareContext

这里会实例化TomcatEmbeddedContext并对其进行配置。

    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

这个方法很简单,只是直接实例化了TomcatWebServer返回。其构造方法触发了initialize,这会引起后续一系列动作,包括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()

我们回过头来,看一下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启动。

selfInitialize

获取到TomcatWebServer后,Tomcat启动之后就触发了selfInitialize方法。这里servletContext其实是获取了ApplicationContext的一个门面/外观–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

    ServletWebServerApplicationContextprepareWebApplicationContext方法如下所示,简单来讲就是为servletContext设置根容器属性并为当前应用上下文ApplicationContext设置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);
                // 这个日志是不是很熟悉?!
                servletContext.log("Initializing Spring embedded WebApplicationContext");
                try {
        
        //向servletContext设置属性 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设置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: ServletWebServerApplicationContextregisterApplicationScope方法如下所示,简单来讲就是(扩展)注册scope-application。这里会实例化一个ServletContextScope (包装了servletContext),然后注册到BeanFactory中并为servletContext设置属性。
        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);
        }
我们在Spring中refresh分析之postProcessBeanFactory方法详解提到了request-RequestScope,session–SessionScope的注册,本文这里注册了application-ServletContextScope注册。
  • 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作为单例注册容器
                if (servletContext != null && !bf.containsBean(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME)) {
                        bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext);
                }
        
        // 将servletConfig 作为单例注册容器本文这里没有触发
                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的初始化参数
                                Enumeration<?> paramNameEnum = servletContext.getInitParameterNames();
                                while (paramNameEnum.hasMoreElements()) {
                                        String paramName = (String) paramNameEnum.nextElement();
                                        parameterMap.put(paramName, servletContext.getInitParameter(paramName));
                                }
                        }
                        // 本文这里servletConfig 为null
                        if (servletConfig != null) {
                        // // 获取servletConfig的初始化参数
                                Enumeration<?> paramNameEnum = servletConfig.getInitParameterNames();
                                while (paramNameEnum.hasMoreElements()) {
                                        String paramName = (String) paramNameEnum.nextElement();
                                        parameterMap.put(paramName, servletConfig.getInitParameter(paramName));
                                }
                        }
                        // 将contextParameters作为单例注册到容器
                        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作为单例注册到容器
                        bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME,
                                        Collections.unmodifiableMap(attributeMap));
                }
        }
  • 触发ServletContextInitializer的onStartup:如下所示,这里会获取ServletContextInitializer的所有实例,遍历触发其onStartup方法。
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
                beans.onStartup(servletContext);
        }
如下所示,将会挨个触发这5个的onStartup方法:

![202301012004229153.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);
        }
在这里,`会将Dispatcher加入到Tomcat中`,之后就是Tomcat的启动流程,跟之前启动SpringMVC流程一样,参见:[Spring5源码14-SpringMVC入口及启动流程][Spring5_14-SpringMVC]

3.6 afterRefresh

进入org.springframework.boot.SpringApplication#afterRefresh方法中

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

这里啥也没做,就是一个模板方法,可能使用于扩展性的吧

3.7 callRunners

进入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对象并调用对应的run方法。

3.8 流程框图

202301012004238004.png

参考文章

Spring5源码注释github地址
Spring源码深度解析(第2版)
spring源码解析
Spring源码深度解析笔记
Spring注解与源码分析
Spring注解驱动开发B站教程