spring boot 2.1.7启动过程源码解析

本文详细解析了Spring Boot的启动流程,从main方法开始,逐步介绍了环境准备、初始化ApplicationContext、加载配置、注册监听器、创建Spring Cloud容器、处理各种事件,以及日志系统初始化等关键步骤。通过分析,揭示了Spring Boot如何加载bootstrap.yaml和application.yaml,以及如何处理远程配置和日志系统。最后,文章总结了Spring Boot启动过程中的扩展点和事件调用逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

约定

    本文基于spring boot 2.1.7.RELEASE进行剖析,使用的spring cloud为Greenwich.SR6版本,github仓库为:spring boot演示。该仓库有多个子模块,下文使用的是consumer子模块。
    术语约定:

  • spring boot容器,main方法启动的spring boot ApplicationContext,也就是用户接触到的容器。
  • spring cloud容器,spring cloud ApplicationContext,也称为父容器,因为spring cloud ApplicationContext最后会作为spring boot ApplicationContext的父容器。
  • 代码埋点,为了方便对某个代码进行讲述,这里标记了一些数字,如果找不到,可以全局搜索下。

    spring boot入门可以看这篇:spring boot入门

1. SpringApplication入口

    执行入口如下。

@SpringBootApplication
public class ConsumerApplication {
	public static void main(String[] args) {
		SpringApplication.run(ConsumerApplication.class, args);
	}
}

    最后会运行new SpringApplication(primarySources).run(args),所以核心方法是SpringApplication的run方法

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

	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 根据类路径中的类是否存在,进而确定ApplicationContext类型,例如存在DispatcherSerlver,则是servlet。
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 从META-INF/spring.factories中,获取对应的ApplicationContextInitializer配置
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 从META-INF/spring.factories中,获取对应的ApplicationListener配置
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		// 埋点(1) 加载EventListener,并转发starting事件
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 埋点(2) 创建environment
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			// 埋点(3) 打印banner
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			// 埋点(4) 初始化ApplicationContext,并设置environment,运行initializer,并注册primarySource类为BeanDefinitionRegistry
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			// 埋点(5) 调用ApplicationContext.refresh
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			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;
	}

2. 获取SpringApplicationRunListener

    在上面埋点(1)中,SpringApplicationRunListeners会从所有依赖的jar的META-INF/spring.factories文件中获取SpringApplicationRunListener的所有实现类,这里是spring boot的SPI机制。

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

    spring boot这里添加了一个默认的SpringApplicationRunListener实现类是:EventPublishingRunListener
请添加图片描述

    SpringApplicationRunListeners是SpringApplicationRunListener的复数形式,会将所有事件,直接调用SpringApplicationRunListener方法。

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

	public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

	public void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}

	public void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}

	public void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}

	public void started(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.started(context);
		}
	}

	public void running(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.running(context);
		}
	}

	public void failed(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
			callFailedListener(listener, context, exception);
		}
	}

	private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
			Throwable exception) {
		try {
			listener.failed(context, exception);
		}
		catch (Throwable ex) {
			if (exception == null) {
				ReflectionUtils.rethrowRuntimeException(ex);
			}
			if (this.log.isDebugEnabled()) {
				this.log.error("Error handling failed", ex);
			}
			else {
				String message = ex.getMessage();
				message = (message != null) ? message : "no error message";
				this.log.warn("Error handling failed (" + message + ")");
			}
		}
	}

}

    在SpringApplicationRunListeners中,共定义了7种spring boot应用的事件,并全部转发给SpringApplicationRunListener运行,这7种事件是spring boot的扩展点,基于这些扩展点,spring boot内部实现了日志自动化配置、加载application.yaml等功能。spring cloud实现了设置父ApplicationContext,设置远程配置。

spring boot启动扩展点执行时机EventPublishingRunListener对应发出的事件类型
starting事件SpringApplication启动后,立即执行ApplicationStartingEvent
environmentPrepared事件最重要的事件。在environment创建后触发,可以用来启动spring cloud容器,加载bootstrap.yaml;加载application.yaml;初始化日志系统和日志级别等日志配置ApplicationEnvironmentPreparedEvent
contextPrepared事件在ApplicationContext创建后,调用ApplicationContextInitializer初始化ApplicationContext后触发。目前spring-cloud-context内部没有用到ApplicationContextInitializedEvent
contextLoaded事件在注册完main方法所在配置类为BeanDefinition后触发ApplicationPreparedEvent
started事件刷新ApplicationContext加载非懒加载单例后触发ApplicationStartedEvent
running事件调用完ApplicationRunner和CommandLineRunner后触发,表示spring boot应用启动成功,在这个阶段可以注册到consul上ApplicationReadyEvent
failed事件spring boot启动过程中抛出异常,触发这个事件ApplicationFailedEvent

2.1 EventPublishingRunListener的实现

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		// (1) 将SpringApplication中的listener加入到事件分发器中。
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

	@Override
	public int getOrder() {
		return 0;
	}

    // 前三种事件,ConfigurableApplicationContext还没构建,这时只对SpringApplication.listener生效
	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster
				.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
	}

	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster
				.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}

     // 从ApplicationPreparedEvent开始,将对SpringApplication.listener添加到ConfigurableApplicationContext中,这时不仅仅SpringApplication.listener生效,ConfigurableApplicationContext中的listener也生效
	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
	}

	@Override
	public void started(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
	}

	@Override
	public void running(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
	}

	@Override
	public void failed(ConfigurableApplicationContext context, Throwable exception) {
		ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
		if (context != null && context.isActive()) {
			// Listeners have been registered to the application context so we should
			// use it at this point if we can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all of the context's listeners instead
			if (context instanceof AbstractApplicationContext) {
				for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
						.getApplicationListeners()) {
					this.initialMulticaster.addApplicationListener(listener);
				}
			}
			this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
			this.initialMulticaster.multicastEvent(event);
		}
	}

	private static class LoggingErrorHandler implements ErrorHandler {

		private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);

		@Override
		public void handleError(Throwable throwable) {
			logger.warn("Error calling ApplicationEventListener", throwable);
		}

	}

}

    下面给出完整的EventPublishingRunListener事件的生效情况。从代码可以看出,在ConfigurableApplicationContext构建之前,事件只有SpringApplication.getListeners()能接收。到了contextLoaded,会把SpringApplication.getListeners()添加到ConfigurableApplicationContext中,之后从started事件开始,使用ConfigurableApplicationContext的publishEvent,这个时候ConfigurableApplicationContext中的listener也生效

spring boot启动扩展点执行时机EventPublishingRunListener对应发出的事件类型说明
starting事件SpringApplication启动后,立即执行ApplicationStartingEvent只对SpringApplication.getListeners()生效
environmentPrepared事件最重要的事件。在environment创建后触发,可以用来启动spring cloud容器,加载bootstrap.yaml;加载application.yaml;初始化日志系统和日志级别等日志配置ApplicationEnvironmentPreparedEvent只对SpringApplication.getListeners()生效
contextPrepared事件在ApplicationContext创建后,调用ApplicationContextInitializer初始化ApplicationContext后触发。目前spring-cloud-context内部没有用到ApplicationContextInitializedEvent只对SpringApplication.getListeners()生效
contextLoaded事件在注册完main方法所在配置类为BeanDefinition后触发ApplicationPreparedEvent只对SpringApplication.getListeners()生效
started事件刷新ApplicationContext加载非懒加载单例后触发ApplicationStartedEvent不仅仅SpringApplication.getListeners()生效,ConfigurableApplicationContext中的listener也生效
running事件调用完ApplicationRunner和CommandLineRunner后触发,表示spring boot应用启动成功,在这个阶段可以注册到consul上ApplicationReadyEvent不仅仅SpringApplication.getListeners()生效,ConfigurableApplicationContext中的listener也生效
failed事件spring boot启动过程中抛出异常,触发这个事件ApplicationFailedEvent不仅仅SpringApplication.getListeners()生效,ConfigurableApplicationContext中的listener也生效

    那么,这里SpringApplication.getListeners()都有哪些呢,相信你也有这样的疑问,其实在SpringApplication构造时,就已经通过SPI机制初始化了listeners,之后也可以通过addListeners()手动添加listener

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 从META-INF/spring.factories中,获取对应的ApplicationContextInitializer配置
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 从META-INF/spring.factories中,获取对应的ApplicationListener配置
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
	public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
		this.listeners = new ArrayList<>();
		this.listeners.addAll(listeners);
	}

	/**
	 * Add {@link ApplicationListener}s to be applied to the SpringApplication and
	 * registered with the {@link ApplicationContext}.
	 * @param listeners the listeners to add
	 */
	public void addListeners(ApplicationListener<?>... listeners) {
		this.listeners.addAll(Arrays.asList(listeners));
	}

    通过debug,可以发现有这么些listeners,注意这个顺序,这个顺序是他们的接收顺序,每个listener都可以设置自己的order,spring boot会按照order从小到大排序。
请添加图片描述

0 = {BootstrapApplicationListener@1696} 
1 = {LoggingSystemShutdownListener@1697} 
2 = {ConfigFileApplicationListener@1698} 
3 = {AnsiOutputApplicationListener@1699} 
4 = {LoggingApplicationListener@1700} 
5 = {ClasspathLoggingApplicationListener@1701} 
6 = {BackgroundPreinitializer@1702} 
7 = {DelegatingApplicationListener@1703} 
8 = {RestartListener@1704} 
9 = {ParentContextCloserApplicationListener@1705} 
10 = {ClearCachesApplicationListener@1706} 
11 = {FileEncodingApplicationListener@1707} 
12 = {LiquibaseServiceLocatorApplicationListener@1708} 

    最后,用一张图总结下listener的调用逻辑
请添加图片描述

2.2 starting事件的处理

    这里再回到埋点(1)的地方,这里加载完SpringApplicationRunListener,会调用staring(),从上面的分析,可以知道,最终转化为ApplicationStartingEvent,交给上面13个ApplicationListener进行监听处理。
    实际上,这个阶段是非常早的,只有LoggingApplicationListener会进行处理。主要作用是选定日志系统,根据类路径下相关的类,根据classpath的jar确定了要使用的日志系统,例如logback、log4j

public class LoggingApplicationListener implements GenericApplicationListener {

	private static final ConfigurationPropertyName LOGGING_LEVEL = ConfigurationPropertyName.of("logging.level");

	private static final ConfigurationPropertyName LOGGING_GROUP = ConfigurationPropertyName.of("logging.group");

	private static final Bindable<Map<String, String>> STRING_STRING_MAP = Bindable.mapOf(String.class, String.class);

	private static final Bindable<Map<String, String[]>> STRING_STRINGS_MAP = Bindable.mapOf(String.class,
			String[].class);

	/**
	 * The default order for the LoggingApplicationListener.
	 */
	public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 20;

	/**
	 * The name of the Spring property that contains a reference to the logging
	 * configuration to load.
	 */
	public static final String CONFIG_PROPERTY = "logging.config";

	/**
	 * The name of the Spring property that controls the registration of a shutdown hook
	 * to shut down the logging system when the JVM exits.
	 * @see LoggingSystem#getShutdownHandler
	 */
	public static final String REGISTER_SHUTDOWN_HOOK_PROPERTY = "logging.register-shutdown-hook";

	/**
	 * The name of the {@link LoggingSystem} bean.
	 */
	public static final String LOGGING_SYSTEM_BEAN_NAME = "springBootLoggingSystem";

	/**
	 * The name of the {@link LogFile} bean.
	 */
	public static final String LOGFILE_BEAN_NAME = "springBootLogFile";

	private static final Map<String, List<String>> DEFAULT_GROUP_LOGGERS;
	static {
		MultiValueMap<String, String> loggers = new LinkedMultiValueMap<>();
		loggers.add("web", "org.springframework.core.codec");
		loggers.add("web", "org.springframework.http");
		loggers.add("web", "org.springframework.web");
		loggers.add("web", "org.springframework.boot.actuate.endpoint.web");
		loggers.add("web", "org.springframework.boot.web.servlet.ServletContextInitializerBeans");
		loggers.add("sql", "org.springframework.jdbc.core");
		loggers.add("sql", "org.hibernate.SQL");
		DEFAULT_GROUP_LOGGERS = Collections.unmodifiableMap(loggers);
	}

	private static final Map<LogLevel, List<String>> LOG_LEVEL_LOGGERS;
	static {
		MultiValueMap<LogLevel, String> loggers = new LinkedMultiValueMap<>();
		loggers.add(LogLevel.DEBUG, "sql");
		loggers.add(LogLevel.DEBUG, "web");
		loggers.add(LogLevel.DEBUG, "org.springframework.boot");
		loggers.add(LogLevel.TRACE, "org.springframework");
		loggers.add(LogLevel.TRACE, "org.apache.tomcat");
		loggers.add(LogLevel.TRACE, "org.apache.catalina");
		loggers.add(LogLevel.TRACE, "org.eclipse.jetty");
		loggers.add(LogLevel.TRACE, "org.hibernate.tool.hbm2ddl");
		LOG_LEVEL_LOGGERS = Collections.unmodifiableMap(loggers);
	}

	private static final Class<?>[] EVENT_TYPES = { ApplicationStartingEvent.class,
			ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class, ContextClosedEvent.class,
			ApplicationFailedEvent.class };

	private static final Class<?>[] SOURCE_TYPES = { SpringApplication.class, ApplicationContext.class };

	private static final AtomicBoolean shutdownHookRegistered = new AtomicBoolean(false);

	private final Log logger = LogFactory.getLog(getClass());

	private LoggingSystem loggingSystem;

	private LogFile logFile;

	private int order = DEFAULT_ORDER;

	private boolean parseArgs = true;

	private LogLevel springBootLogging = null;

	@Override
	public boolean supportsEventType(ResolvableType resolvableType) {
		return isAssignableFrom(resolvableType.getRawClass(), EVENT_TYPES);
	}

	@Override
	public boolean supportsSourceType(Class<?> sourceType) {
		return isAssignableFrom(sourceType, SOURCE_TYPES);
	}

	private boolean isAssignableFrom(Class<?> type, Class<?>... supportedTypes) {
		if (type != null) {
			for (Class<?> supportedType : supportedTypes) {
				if (supportedType.isAssignableFrom(type)) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationStartingEvent) {
			onApplicationStartingEvent((ApplicationStartingEvent) event);
		}
		else if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		else if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent((ApplicationPreparedEvent) event);
		}
		else if (event instanceof ContextClosedEvent
				&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
			onContextClosedEvent();
		}
		else if (event instanceof ApplicationFailedEvent) {
			onApplicationFailedEvent();
		}
	}

   // 选定日志系统,根据classpath的jar确定用logback还是log4j
	private void onApplicationStartingEvent(ApplicationStartingEvent event) {
		this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
		this.loggingSystem.beforeInitialize();
	}

	private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
		if (this.loggingSystem == null) {
			this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
		}
		initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
	}

	private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
		ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
		if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
			beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
		}
		if (this.logFile != null && !beanFactory.containsBean(LOGFILE_BEAN_NAME)) {
			beanFactory.registerSingleton(LOGFILE_BEAN_NAME, this.logFile);
		}
	}
}

3. 创建spring boot environment

    根据web应用类型,创建environment,这时已经有了properties/env等source,然后会把命令行参数添加到第一位,SimpleCommandLinePropertySource。

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 添加命令行参数
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

    之后触发environmentPrepared事件,也就是ApplicationEnvironmentPreparedEvent。根据order来,处理这个事件的有BootstrapApplicationListener,之后LoggingSystemShutdownListener,再调用ConfigFileApplicationListener,最后LoggingApplicationListener初始化日志系统。最后使用ConfigurationPropertySources.attach,保证支持spring boot宽松绑定配置。

3.1 环境预初始化阶段之BootstrapApplicationListener

3.1.1 初始化spring cloud容器

    spring-cloud-context中的BootstrapApplicationListener,只处理ApplicationEnvironmentPreparedEvent,创建spring cloud容器。见:org.springframework.cloud.bootstrap.BootstrapApplicationListener#bootstrapServiceContext

public class BootstrapApplicationListener
		implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

	/**
	 * Property source name for bootstrap.
	 */
	public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap";

	/**
	 * The default order for this listener.
	 */
	public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;

	/**
	 * The name of the default properties.
	 */
	public static final String DEFAULT_PROPERTIES = "defaultProperties";

	private int order = DEFAULT_ORDER;

	@Override
	public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
		ConfigurableEnvironment environment = event.getEnvironment();
		if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
				true)) {
			return;
		}
		// don't listen to events in a bootstrap context
		// boostrap容器初始化的过程中也会调用BootstrapApplicationListener,不处理boostrap容器的事件,防止重入,这里通过是否包含source name来判断,而不通过ApplicationContext,因为ApplicationContext这个时候还没构建
		if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
			return;
		}
		ConfigurableApplicationContext context = null;
		/// 通过spring.cloud.bootstrap.name设置配置文件名,默认是bootstrap,可以通过命令行/环境变量等设置,此时application.yaml还会加载
		String configName = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
		for (ApplicationContextInitializer<?> initializer : event.getSpringApplication()
				.getInitializers()) {
			if (initializer instanceof ParentContextApplicationContextInitializer) {
				context = findBootstrapContext(
						(ParentContextApplicationContextInitializer) initializer,
						configName);
			}
		}
		if (context == null) {
		   // 构建bootstrap容器,这里默认设置了配置文件名为bootstrap,因而配置文件名为bootstrap.yaml/bootstrap.properties
			context = bootstrapServiceContext(environment, event.getSpringApplication(),
					configName);
			event.getSpringApplication()
					.addListeners(new CloseContextOnFailureApplicationListener(context));
		}

        //埋点(6)将spring cloud 所有的ApplicationContextInitializer类型的bean,添加到spring boot application中
		apply(context, event.getSpringApplication(), environment);
	}
}

    这里详细看下如何构建spring cloud容器。

// 
	private ConfigurableApplicationContext bootstrapServiceContext(
			ConfigurableEnvironment environment, final SpringApplication application,
			String configName) {
		StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
		MutablePropertySources bootstrapProperties = bootstrapEnvironment
				.getPropertySources();
		for (PropertySource<?> source : bootstrapProperties) {
			bootstrapProperties.remove(source.getName());
		}
		// 设置spring cloud配置文件目录
		String configLocation = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
		String configAdditionalLocation = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.additional-location:}");
		Map<String, Object> bootstrapMap = new HashMap<>();
		bootstrapMap.put("spring.config.name", configName);
		// if an app (or test) uses spring.main.web-application-type=reactive, bootstrap
		// will fail
		// force the environment to use none, because if though it is set below in the
		// builder
		// the environment overrides it
		bootstrapMap.put("spring.main.web-application-type", "none");
		if (StringUtils.hasText(configLocation)) {
			bootstrapMap.put("spring.config.location", configLocation);
		}
		if (StringUtils.hasText(configAdditionalLocation)) {
			bootstrapMap.put("spring.config.additional-location",
					configAdditionalLocation);
		}
		// 添加BOOTSTRAP_PROPERTY_SOURCE_NAME作为source
		bootstrapProperties.addFirst(
				new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
		for (PropertySource<?> source : environment.getPropertySources()) {
			if (source instanceof StubPropertySource) {
				continue;
			}
			bootstrapProperties.addLast(source);
		}
		// TODO: is it possible or sensible to share a ResourceLoader?
		// 通过SpringApplication构建spring cloud容器,不打印banner
		SpringApplicationBuilder builder = new SpringApplicationBuilder()
				.profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
				.environment(bootstrapEnvironment)
				// Don't use the default properties in this builder
				.registerShutdownHook(false).logStartupInfo(false)
				.web(WebApplicationType.NONE);
		final SpringApplication builderApplication = builder.application();
		if (builderApplication.getMainApplicationClass() == null) {
			// gh_425:
			// SpringApplication cannot deduce the MainApplicationClass here
			// if it is booted from SpringBootServletInitializer due to the
			// absense of the "main" method in stackTraces.
			// But luckily this method's second parameter "application" here
			// carries the real MainApplicationClass which has been explicitly
			// set by SpringBootServletInitializer itself already.
			builder.main(application.getMainApplicationClass());
		}
		// 对于refreshEndpoint等事件触发的ApplicationContext重新构建,environment会包含refreshArgs source,这里把日志相关的listener过滤掉,防止重新设置日志系统
		if (environment.getPropertySources().contains("refreshArgs")) {
			// If we are doing a context refresh, really we only want to refresh the
			// Environment, and there are some toxic listeners (like the
			// LoggingApplicationListener) that affect global static state, so we need a
			// way to switch those off.
			builderApplication
					.setListeners(filterListeners(builderApplication.getListeners()));
		}
		// 埋点(7) 设置spring cloud的config类
		builder.sources(BootstrapImportSelectorConfiguration.class);
		final ConfigurableApplicationContext context = builder.run();
		// gh-214 using spring.application.name=bootstrap to set the context id via
		// `ContextIdApplicationContextInitializer` prevents apps from getting the actual
		// spring.application.name
		// during the bootstrap phase.
		context.setId("bootstrap");
		// Make the bootstrap context a parent of the app context
		// 埋点(8) 添加AncestorInitializer初始化器到外部SpringApplication,从而将spring cloud容器设置为外部SpringApplication对应的容器的父容器
		addAncestorInitializer(application, context);
		// It only has properties in it now that we don't want in the parent so remove
		// it (and it will be added back later)
		bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
		// 埋点(9),将spring cloud application构建后得到的bootstrap.properties,添加到子容器的环境中
		mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
		return context;
	}

    从埋点(7),我们可以看到spring cloud application对应的容器使用的配置类是BootstrapImportSelectorConfiguration,该类会导入BootstrapImportSelector。BootstrapImportSelector通过spring boot SPI,导入BootstrapConfiguration作为key的所有扩展点作为spring cloud容器的bean。且默认使用的配置文件为bootstrap.yaml/bootstrap.properties

@Configuration
@Import(BootstrapImportSelector.class)
public class BootstrapImportSelectorConfiguration {

}

public class BootstrapImportSelector implements EnvironmentAware, DeferredImportSelector {

	private Environment environment;

	private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		List<String> names = new ArrayList<>(SpringFactoriesLoader
				.loadFactoryNames(BootstrapConfiguration.class, classLoader));
		names.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(
				this.environment.getProperty("spring.cloud.bootstrap.sources", ""))));

		List<OrderedAnnotatedElement> elements = new ArrayList<>();
		for (String name : names) {
			try {
				elements.add(
						new OrderedAnnotatedElement(this.metadataReaderFactory, name));
			}
			catch (IOException e) {
				continue;
			}
		}
		AnnotationAwareOrderComparator.sort(elements);

		String[] classNames = elements.stream().map(e -> e.name).toArray(String[]::new);

		return classNames;
	}
}

    spring-cloud-context中的所有BootstrapConfiguration自动化配置类如下:

请添加图片描述
    BootstrapConfiguration这里注入了很多spring cloud的基础设施,例如远程配置consul/nacos。相关基础设置通过BootstrapConfiguration设置到spring cloud容器中,最后外部spring boot容器也能拿到相关bean。注意这里读取的配置文件是bootstrap.yaml,设置到application.yaml不生效,此时application.yaml还没加载!!! 通过spring.cloud.bootstrap.name可以设置spring cloud配置文件名,默认是bootstrap,这里只能通过命令行/环境变量等设置啦。
(1) consul
请添加图片描述

(2) nacos
请添加图片描述

3.1.2 添加AncestorInitializer初始化器到spring boot SpringApplication

     在上面的埋点(8) ,默认会添加AncestorInitializer初始化器到外部SpringApplication,当外部SpringApplication运行ApplicationContextInitializer时,会将spring cloud容器设置为外部SpringApplication对应的容器的父容器

// BootstrapApplicationListener
	private void addAncestorInitializer(SpringApplication application,
			ConfigurableApplicationContext context) {
		boolean installed = false;
		for (ApplicationContextInitializer<?> initializer : application
				.getInitializers()) {
			if (initializer instanceof AncestorInitializer) {
				installed = true;
				// New parent
				// 	如果父类中有AncestorInitializer的初始化器,修改父context
				((AncestorInitializer) initializer).setParent(context);
			}
		}
		if (!installed) {
		// 如果父类中没有AncestorInitializer的初始化器,则添加AncestorInitializer
			application.addInitializers(new AncestorInitializer(context));
		}
	}
	private static class AncestorInitializer implements
			ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {

		private ConfigurableApplicationContext parent;

		AncestorInitializer(ConfigurableApplicationContext parent) {
			this.parent = parent;
		}

		public void setParent(ConfigurableApplicationContext parent) {
			this.parent = parent;
		}

		@Override
		public int getOrder() {
			// Need to run not too late (so not unordered), so that, for instance, the
			// ContextIdApplicationContextInitializer runs later and picks up the merged
			// Environment. Also needs to be quite early so that other initializers can
			// pick up the parent (especially the Environment).
			return Ordered.HIGHEST_PRECEDENCE + 5;
		}

		@Override
		public void initialize(ConfigurableApplicationContext context) {
			while (context.getParent() != null && context.getParent() != context) {
				context = (ConfigurableApplicationContext) context.getParent();
			}
			// 将spring cloud defaultProperties调整到最后,同时将defaultProperties包含的bootstrap.yaml添加到defaultProperties前面,也就是倒数第二
			reorderSources(context.getEnvironment());
			// 将spring cloud context设置为当前容器的父容器
			new ParentContextApplicationContextInitializer(this.parent)
					.initialize(context);
		}

		private void reorderSources(ConfigurableEnvironment environment) {
			PropertySource<?> removed = environment.getPropertySources()
					.remove(DEFAULT_PROPERTIES);
			if (removed instanceof ExtendedDefaultPropertySource) {
				ExtendedDefaultPropertySource defaultProperties = (ExtendedDefaultPropertySource) removed;
				environment.getPropertySources().addLast(new MapPropertySource(
						DEFAULT_PROPERTIES, defaultProperties.getSource()));
				for (PropertySource<?> source : defaultProperties.getPropertySources()
						.getPropertySources()) {
					if (!environment.getPropertySources().contains(source.getName())) {
						environment.getPropertySources().addBefore(DEFAULT_PROPERTIES,
								source);
					}
				}
			}
		}

	}
3.1.3 将spring cloud application构建后得到的bootstrap.properties,添加到spring boot子容器的environment

    上面埋点(9),将spring cloud application构建后得到的properties,添加到子容器的环境中,name为defaultProperties,主要是bootstrap.yaml等追加到spring boot environment中。

// BootstrapApplicationListener
	private void mergeAdditionalPropertySources(MutablePropertySources environment,
			MutablePropertySources bootstrap) {
		PropertySource<?> defaultProperties = environment.get(DEFAULT_PROPERTIES);
		ExtendedDefaultPropertySource result = defaultProperties instanceof ExtendedDefaultPropertySource
				? (ExtendedDefaultPropertySource) defaultProperties
				: new ExtendedDefaultPropertySource(DEFAULT_PROPERTIES,
						defaultProperties);
						// 过滤出bootstrap有,而spring boot environment没有的source,作为default
		for (PropertySource<?> source : bootstrap) {
			if (!environment.contains(source.getName())) {
				result.add(source);
			}
		}
		for (String name : result.getPropertySourceNames()) {
		// spring cloud environment移除了bootstrap.yaml
			bootstrap.remove(name);
		}
		// bootstrap中特有的bootstrap.yaml,追加到spring boot environment中
		addOrReplace(environment, result);
		// spring cloud environment重新添加bootstrap.yaml。。。
		addOrReplace(bootstrap, result);
	}
3.1.4 将spring cloud 所有的ApplicationContextInitializer类型的bean,添加到spring boot application中执行

     埋点(6)将spring cloud 所有的ApplicationContextInitializer类型的bean,添加到spring boot application中。

// BootstrapApplicationListener
	private void apply(ConfigurableApplicationContext context,
			SpringApplication application, ConfigurableEnvironment environment) {
		@SuppressWarnings("rawtypes")
		List<ApplicationContextInitializer> initializers = getOrderedBeansOfType(context,
				ApplicationContextInitializer.class);
		application.addInitializers(initializers
				.toArray(new ApplicationContextInitializer[initializers.size()]));
		addBootstrapDecryptInitializer(application);
	}

     其中spring-cloud-context自带的配置类为PropertySourceBootstrapConfiguration,这个类实现了ApplicationContextInitializer,其initialize会从spring cloud容器中找到所有的PropertySourceLocator bean,利用PropertySourceLocator对spring boot容器的environment进行初始化。注意,这里autowired=false,允许不存在PropertySourceLocator对应的bean。

@Configuration
@EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
public class PropertySourceBootstrapConfiguration implements
		ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {

	/**
	 * Bootstrap property source name.
	 */
	public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME
			+ "Properties";

	private static Log logger = LogFactory
			.getLog(PropertySourceBootstrapConfiguration.class);

	private int order = Ordered.HIGHEST_PRECEDENCE + 10;

// 从spring cloud容器中找到所有的PropertySourceLocator bean,并注入
	@Autowired(required = false)
	private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>();

	public void setPropertySourceLocators(
			Collection<PropertySourceLocator> propertySourceLocators) {
		this.propertySourceLocators = new ArrayList<>(propertySourceLocators);
	}

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		List<PropertySource<?>> composite = new ArrayList<>();
		AnnotationAwareOrderComparator.sort(this.propertySourceLocators);
		boolean empty = true;
		ConfigurableEnvironment environment = applicationContext.getEnvironment();
		for (PropertySourceLocator locator : this.propertySourceLocators) {
			Collection<PropertySource<?>> source = locator.locateCollection(environment);
			if (source == null || source.size() == 0) {
				continue;
			}
			List<PropertySource<?>> sourceList = new ArrayList<>();
			for (PropertySource<?> p : source) {
				sourceList.add(new BootstrapPropertySource<>(p));
			}
			logger.info("Located property source: " + sourceList);
			composite.addAll(sourceList);
			empty = false;
		}
		if (!empty) {
			MutablePropertySources propertySources = environment.getPropertySources();
			String logConfig = environment.resolvePlaceholders("${logging.config:}");
			LogFile logFile = LogFile.get(environment);
			for (PropertySource<?> p : environment.getPropertySources()) {
				if (p.getName().startsWith(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
					propertySources.remove(p.getName());
				}
			}
			// 埋点(10) 将PropertySourceLocator加载的所有的外部配置,添加到spring boot容器的enviroment中
			insertPropertySources(propertySources, composite);
			// 重新初始化日志系统和日志级别
			reinitializeLoggingSystem(environment, logConfig, logFile);
			setLogLevels(applicationContext, environment);
			handleIncludedProfiles(environment);
		}
	}
}

    对于spring cloud容器而言,PropertySourceLocator是一个扩展点,可以加载远程配置,consul/nacos等均基于这个扩展点加载远程配置。埋点(10) 将spring cloud PropertySourceLocator加载的所有的外部配置,添加到spring boot容器的environment中(注意这些ApplicationContextInitializer在spring boot容器执行!!),默认加在最前面,最后根据远程配置,重新初始化日志系统和日志级别。远程配置在命令行参数之前。
    如果要修改远程优先,有两种方法,一种是修改远程配置;另一种是修改bootstrap.yaml中consul/nacos中的配置,使得读取远程失败。

// PropertySourceBootstrapConfiguration
	private void insertPropertySources(MutablePropertySources propertySources,
			List<PropertySource<?>> composite) {
		MutablePropertySources incoming = new MutablePropertySources();
		List<PropertySource<?>> reversedComposite = new ArrayList<>(composite);
		// Reverse the list so that when we call addFirst below we are maintaining the
		// same order of PropertySources
		// Wherever we call addLast we can use the order in the List since the first item
		// will end up before the rest
		// 倒序,因为下面addFirst是头插法,保证最后是正序的,
		Collections.reverse(reversedComposite);
		for (PropertySource<?> p : reversedComposite) {
			incoming.addFirst(p);
		}
		PropertySourceBootstrapProperties remoteProperties = new PropertySourceBootstrapProperties();
		Binder.get(environment(incoming)).bind("spring.cloud.config",
				Bindable.ofInstance(remoteProperties));
				// 默认情况下外部配置加到最前面
		if (!remoteProperties.isAllowOverride() || (!remoteProperties.isOverrideNone()
				&& remoteProperties.isOverrideSystemProperties())) {
			for (PropertySource<?> p : reversedComposite) {
				propertySources.addFirst(p);
			}
			return;
		}
}

    consul注入ConsulPropertySourceLocator

@Configuration
@ConditionalOnConsulEnabled
public class ConsulConfigBootstrapConfiguration {
    public ConsulConfigBootstrapConfiguration() {
    }

    @Configuration
    @EnableConfigurationProperties
    @Import({ConsulAutoConfiguration.class})
    @ConditionalOnProperty(
        name = {"spring.cloud.consul.config.enabled"},
        matchIfMissing = true
    )
    protected static class ConsulPropertySourceConfiguration {
        @Autowired
        private ConsulClient consul;

        protected ConsulPropertySourceConfiguration() {
        }

        @Bean
        public ConsulConfigProperties consulConfigProperties() {
            return new ConsulConfigProperties();
        }

        @Bean
        public ConsulConfigCacheProperties consulConfigCacheProperties() {
            return new ConsulConfigCacheProperties();
        }

        @Bean
        public ConsulConfigCacheClient consulConfigCacheClient(ConsulConfigCacheProperties consulConfigCacheProperties) {
            return new ConsulConfigCacheClient(consulConfigCacheProperties);
        }

        @Bean
        public ConsulPropertySourceLocator consulPropertySourceLocator(ConsulConfigProperties consulConfigProperties, ConsulConfigCacheClient consulConfigCacheClient) {
            return new ConsulPropertySourceLocator(this.consul, consulConfigProperties, consulConfigCacheClient);
        }
    }
}

    nacos注入ConsulPropertySourceLocator

@Configuration
@ConditionalOnProperty(
    name = {"spring.cloud.nacos.config.enabled"},
    matchIfMissing = true
)
public class NacosConfigBootstrapConfiguration {
    public NacosConfigBootstrapConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public NacosConfigProperties nacosConfigProperties() {
        return new NacosConfigProperties();
    }

    @Bean
    public NacosPropertySourceLocator nacosPropertySourceLocator(NacosConfigProperties nacosConfigProperties) {
        return new NacosPropertySourceLocator(nacosConfigProperties);
    }
}

3.2 环境预初始化阶段之LoggingSystemShutdownListener

    这个listener是spring-cloud-context引进来的,主要是为了重新确定使用的日志系统。。。

public class LoggingSystemShutdownListener
		implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

	/**
	 * Default order for the listener.
	 */
	public static final int DEFAULT_ORDER = BootstrapApplicationListener.DEFAULT_ORDER
			+ 1;

	private int order = DEFAULT_ORDER;

	@Override
	public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
		shutdownLogging();
	}

	private void shutdownLogging() {
	// 清理并重新初始化日志系统
		LoggingSystem loggingSystem = LoggingSystem
				.get(ClassUtils.getDefaultClassLoader());
		loggingSystem.cleanUp();
		loggingSystem.beforeInitialize();
	}

	@Override
	public int getOrder() {
		return this.order;
	}

	public void setOrder(int order) {
		this.order = order;
	}

}

3.3 环境预初始化阶段之ConfigFileApplicationListener

    spring boot使用ConfigFileApplicationListener来加载application.yaml,application.yaml位于defaultProperties(bootstrap.yaml)之前。ConfigFileApplicationListener实现了EnvironmentPostProcessor,在postProcessEnvironment方法中对environment进下增强。

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {

	private static final String DEFAULT_PROPERTIES = "defaultProperties";

	// Note the order is from least to most specific (last one wins)
	private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";

	private static final String DEFAULT_NAMES = "application";

	private static final Set<String> NO_SEARCH_NAMES = Collections.singleton(null);

	private static final Bindable<String[]> STRING_ARRAY = Bindable.of(String[].class);

	/**
	 * The "active profiles" property name.
	 */
	public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";

	/**
	 * The "includes profiles" property name.
	 */
	public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";

	/**
	 * The "config name" property name.
	 */
	public static final String CONFIG_NAME_PROPERTY = "spring.config.name";

	/**
	 * The "config location" property name.
	 */
	public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";

	/**
	 * The "config additional location" property name.
	 */
	public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";

	/**
	 * The default order for the processor.
	 */
	public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;

	private final DeferredLog logger = new DeferredLog();

	private String searchLocations;

	private String names;

	private int order = DEFAULT_ORDER;

	@Override
	public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
		return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType)
				|| ApplicationPreparedEvent.class.isAssignableFrom(eventType);
	}

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

	private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
		List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
		// 这里把自己放到第一位
		postProcessors.add(this);
		AnnotationAwareOrderComparator.sort(postProcessors);
		for (EnvironmentPostProcessor postProcessor : postProcessors) {
			postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
		}
	}

	List<EnvironmentPostProcessor> loadPostProcessors() {
		return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
	}

	@Override
	public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
		addPropertySources(environment, application.getResourceLoader());
	}

3.4 环境预初始化阶段之LoggingApplicationListener

    在application.yaml加载之后,这里设置日志级别和日志文件

    private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        if (this.loggingSystem == null) {
            this.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());
        }

        this.initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader());
    }
    protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) {
        (new LoggingSystemProperties(environment)).apply();
        this.logFile = LogFile.get(environment);
        if (this.logFile != null) {
            this.logFile.applyToSystemProperties();
        }

        this.initializeEarlyLoggingLevel(environment);
        this.initializeSystem(environment, this.loggingSystem, this.logFile);
        this.initializeFinalLoggingLevels(environment, this.loggingSystem);
        this.registerShutdownHookIfNecessary(environment, this.loggingSystem);
    }

4. 打印spring boot banner欢迎条

    如果不设置的话,默认使用SpringBootBanner中的欢迎条,会添加上当前spring boot版本。

class SpringBootBanner implements Banner {

	private static final String[] BANNER = { "", "  .   ____          _            __ _ _",
			" /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
			" \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )", "  '  |____| .__|_| |_|_| |_\\__, | / / / /",
			" =========|_|==============|___/=/_/_/_/" };

	private static final String SPRING_BOOT = " :: Spring Boot :: ";

	private static final int STRAP_LINE_SIZE = 42;

	@Override
	public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
		for (String line : BANNER) {
			printStream.println(line);
		}
		// 追加版本
		String version = SpringBootVersion.getVersion();
		version = (version != null) ? " (v" + version + ")" : "";
		StringBuilder padding = new StringBuilder();
		while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
			padding.append(" ");
		}

		printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
				AnsiStyle.FAINT, version));
		printStream.println();
	}

}

5. 初始化spring boot ApplicationContext,并设置environment,运行ApplicationContextInitializer

    默认情况下,如果路径下有DispatcherServlet,则webApplicationType为SERVLET,使用的ApplicationContext为AnnotationConfigServletWebServerApplicationContext。在埋点(4)中,会把environment设置到ApplicationContext中,然后使用所有添加到SpringApplication的ApplicationContextInitializer,对ApplicationContext进行初始化

	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		// 使用所有添加到SpringApplication的ApplicationContextInitializer,对ApplicationContext进行初始化
		applyInitializers(context);
		// 触发contextPrepared
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// Load the sources
		// 加载所有的配置类,注册为beandefinition,通过main方法传递过来的
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
				// 触发contextLoaded
		listeners.contextLoaded(context);
	}

    本实例中,有如下ApplicationContextInitializer,这里重点讲解两个

请添加图片描述
    一个是AncestorInitializer,在3.1.2中已经讲到了,主要是将spring cloud defaultProperties调整到spring boot environment最后,同时将defaultProperties包含的bootstrap.yaml添加到defaultProperties前面,也就是倒数第二,将spring cloud context设置为spring boot context的parent。自此,spring boot容器有办法访问到spring cloud基础设施相关bean

    一个是PropertySourceBootstrapConfiguration,在3.1.4已经讲到了,将spring cloud PropertySourceLocator加载的所有的外部配置,添加到spring boot容器的environment中,默认加在最前面,最后根据远程配置,重新初始化日志系统和日志级别。
请添加图片描述
请添加图片描述

5.1 触发contextPrepared

    ApplicationContextInitializedEvent
这个事件目前没看到spring cloud context内部有用到

5.2 load bean definition

    加载所有的配置类,注册为bean definition,通过main方法传递过来的,则加载main所在配置类。

5.3 触发contextLoaded

    ApplicationPreparedEvent,在LoggingApplicationListener会注册日志系统作为单例bean。

	private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
		ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
		if (!beanFactory.containsBean(LOGGING_SYSTEM_BEAN_NAME)) {
			beanFactory.registerSingleton(LOGGING_SYSTEM_BEAN_NAME, this.loggingSystem);
		}
		if (this.logFile != null && !beanFactory.containsBean(LOGFILE_BEAN_NAME)) {
			beanFactory.registerSingleton(LOGFILE_BEAN_NAME, this.logFile);
		}
	}

    ApplicationPreparedEvent,在ConfigFileApplicationListener会添加PropertySourceOrderingPostProcessor

	private void onApplicationPreparedEvent(ApplicationEvent event) {
		this.logger.switchTo(ConfigFileApplicationListener.class);
		addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
	}

	/**
	 * Add appropriate post-processors to post-configure the property-sources.
	 * @param context the context to configure
	 */
	protected void addPostProcessors(ConfigurableApplicationContext context) {
		context.addBeanFactoryPostProcessor(new PropertySourceOrderingPostProcessor(context));
	}

6. 刷新ApplicationContext,初始化单例bean

    这里refresh前,所有的配置文件均已加载完毕,包括bootstrap.yaml/application.yaml,远程配置已经放到第一位。
    在main类上,我们标注了@SpringBootApplication,事实上,这个注解是一个组合注解,功能由其他注解构成,主要是@ComponentScan和@EnableAutoConfiguration。初始化所有单例前,先使用@ComponentScan默认扫描main所在的包的类,注册为bean;之后会加载自动化配置类,处理对应的@Bean方法,注册为bean

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

6.1 扫描main类所在包下的业务类,并注册成bean

    @ComponentScan默认会扫描main类所在包下的bean,这里主要是业务自己定义的@Component的类。
请添加图片描述

6.2 加载并处理自动化配置类

    @EnableAutoConfiguration注解上有@Import,所以底层是通过AutoConfigurationImportSelector导入所有的META-INF/spring.factories中的EnableAutoConfiguration作为key来实现的。注意导入的自动配置类上如果有条件注解,且不满足生效条件,则该自动配置类最终不会添加到ApplicationContext中。另外,@import和条件注解均是spring自带的能力,并非spring boot实现的,spring boot只不过是将其充分利用起来了。类似的注解还有@EnableAsync/@EnableCaching

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

请添加图片描述

6.3 添加shutdown hook

    调用context.registerShutdownHook(),保证spring boot应用收到退出信号时,能够优雅关闭。

6.4 调用Runner

  • 先发送started(ApplicationStartedEvent)事件
  • 之后找到当前ApplicationContext的所有的ApplicationRunner和CommandLineRunner类型的bean,并把main参数传递给它,运行这些runner。

6.5 发送ready事件,spring boot启动完成

  • 最后发送running(ApplicationReadyEvent)事件,spring boot启动完成。在RefreshEventListener会监听这个事件,设置状态为ready,防止spring boot未启动完全就处理environment refresh。这里可以做很多事情,例如将服务实例注册到consul上。
public class RefreshEventListener implements SmartApplicationListener {

	private static Log log = LogFactory.getLog(RefreshEventListener.class);

	private ContextRefresher refresh;

	private AtomicBoolean ready = new AtomicBoolean(false);

	public RefreshEventListener(ContextRefresher refresh) {
		this.refresh = refresh;
	}

	@Override
	public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
		return ApplicationReadyEvent.class.isAssignableFrom(eventType)
				|| RefreshEvent.class.isAssignableFrom(eventType);
	}

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationReadyEvent) {
			handle((ApplicationReadyEvent) event);
		}
		else if (event instanceof RefreshEvent) {
			handle((RefreshEvent) event);
		}
	}

	public void handle(ApplicationReadyEvent event) {
	// spring boot启动后,才设置ready
		this.ready.compareAndSet(false, true);
	}

	public void handle(RefreshEvent event) {
	   // spring boot启动后,设置ready后,才处理,否则忽略
		if (this.ready.get()) { // don't handle events before app is ready
			log.debug("Event received " + event.getEventDesc());
			Set<String> keys = this.refresh.refresh();
			log.info("Refresh keys changed: " + keys);
		}
	}

}

7. 总结

    spring boot整体运行过程如下:
请添加图片描述

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值