Alian解读SpringBoot 2.6.0 源码(四):启动流程分析之应用环境准备

一、背景

  上一篇我们解读了命令行参数解析,本篇主要解读应用环境准备,老样子还是回顾下启动的整体流程,这样就能不迷路。

1.1、run方法整体流程

  接下来的几个方法所在类的具体路径:org.springframework.boot.SpringApplication

	public ConfigurableApplicationContext run(String... args) {
		// 1、记录启动的开始时间(单位纳秒)
		long startTime = System.nanoTime();
		
		// 2、初始化启动上下文、初始化应用上下文
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;

		// 3、设置无头属性:“java.awt.headless”,默认值为:true(没有图形化界面)
		configureHeadlessProperty();

		// 4、获取所有 Spring 运行监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 发布应用启动事件
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			// 5、初始化默认应用参数类(命令行参数)
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

			// 6、根据运行监听器和应用参数 来准备 Spring 环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			// 配置忽略bean信息
			configureIgnoreBeanInfo(environment);

			// 7、创建 Banner 并打印
			Banner printedBanner = printBanner(environment);

			// 8、创建应用上下文
			context = createApplicationContext();
			// 设置applicationStartup
			context.setApplicationStartup(this.applicationStartup);

			// 9、准备应用上下文
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

			// 10、刷新应用上下文(核心)
			refreshContext(context);

			// 11、应用上下文刷新后置处理
			afterRefresh(context, applicationArguments);

			// 13、时间信息、输出日志记录执行主类名
			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}

			// 14、发布应用上下文启动完成事件
			listeners.started(context, timeTakenToStartup);

			// 15、执行所有 Runner 运行器
			callRunners(context, applicationArguments);
		} catch (Throwable ex) {
			// 运行错误处理
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			// 16、发布应用上下文就绪事件(可以使用了)
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			listeners.ready(context, timeTakenToReady);
		} catch (Throwable ex) {
			// 运行错误处理
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		// 17、返回应用上下文
		return context;
	}

1.2、本文解读范围

  结合上一篇解析到的命令行参数,进行环境配置,也就是:

	// 6、根据运行监听器和应用参数 来准备 Spring 环境
	ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
	// 配置忽略bean信息
	configureIgnoreBeanInfo(environment);

二、应用环境准备

2.1、准备环境的整体流程

  此方法所在类的具体路径:org.springframework.boot.SpringApplication

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		// 创建或获取一个环境,主要根据应用程序类型
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		
		// 配置环境(参数时创建的环境和上一篇文章里获取的命令行参数)
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		
		// 附加指定的环境配置,这里是把SpringConfigurationPropertySources加入到环境
		ConfigurationPropertySources.attach(environment);
		
		// 发布环境准备事件(包含application.properties文件的配置项解析,下一篇具体分析)
		listeners.environmentPrepared(bootstrapContext, environment);
		
		DefaultPropertiesPropertySource.moveToEnd(environment);
		Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
		
		// 将获取到的environment中的spring.main配置绑定到SpringApplication的source中
		bindToSpringApplication(environment);
		
		if (!this.isCustomEnvironment) {
			// 根据spring.main.web-application-type配置判断是否需要转换环境
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		// 解除附加的特定的环境配置(避免冲突)
		ConfigurationPropertySources.attach(environment);
		// 返回准备好的环境
		return environment;
	}
  • 根据应用程序类型创建环境
  • 配置环境,主要是属性源的配置,包括命令行参数(如果有)
  • 附加指定的环境配置,此处是SpringConfigurationPropertySources
  • 发布环境准备事件
  • 环境绑定到应用程序
  • 根据应用程序类型转换环境
  • 解除附加环境配置

2.2、创建环境

  此方法所在类的具体路径:org.springframework.boot.SpringApplication,注意本方法不同的版本实现不一样,我这个SpringBoot版本是2.6.0

	private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		switch (this.webApplicationType) {
		case SERVLET:
			// servlet环境
			return new ApplicationServletEnvironment();
		case REACTIVE:
			// 响应式web环境
			return new ApplicationReactiveWebEnvironment();
		default:
			// 标准环境
			return new ApplicationEnvironment();
		}
	}

  从方法名上看,这个方法的作用就是获取或创建环境,存在环境就直接返回,不存在则创建一个并返回。因为我们第一篇文章里,SpringApplication对象构造时推断的应用程序类型是SERVLET,故此处应该是创建一个servlet环境:ApplicationServletEnvironment

  ConfigurableEnvironment 继承于Environment,并且ConfigurableEnvironment还继承了ConfigurablePropertyResolver,而ConfigurablePropertyResolver继承于PropertyResolver

  ConfigurableEnvironment 不仅可以提供了配置文件解析的数据,以及配置文件名称,还提供了PropertySource数据。其实配置文件的解析出来的数据,也是封装成了PropertySource放在ConfigurableEnvironment 中。PropertyResolver 是对于键值属性PropertySource对外数据的统一处理,实现它来获取PropertySource的数据。具体的看类图:

在这里插入图片描述
这里创建完环境得到如下:

ApplicationServletEnvironment {
	activeProfiles=[], 
	defaultProfiles=[default], 
	propertySources=[
		StubPropertySource {name='servletConfigInitParams'}, 
		StubPropertySource {name='servletContextInitParams'}, 
		PropertiesPropertySource {name='systemProperties'}, 
		SystemEnvironmentPropertySource {name='systemEnvironment'}
	]
}

2.3、配置环境

  此方法所在类的具体路径:org.springframework.boot.SpringApplication

	private boolean addConversionService = true;
	
	protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
		// 是否添加转换服务,默认值是true
		if (this.addConversionService) {
			// 设置转换服务,实际获得的是一个共享实例
			environment.setConversionService(new ApplicationConversionService());
		}
		// 配置propertySources
		configurePropertySources(environment, args);
		// 配置profiles
		configureProfiles(environment, args);
	}
2.3.1、注册默认的转换器、格式化组件
2.3.1.1、实例化转换服务

  此方法所在类的具体路径:org.springframework.boot.convert.ApplicationConversionService

public class ApplicationConversionService extends FormattingConversionService {

	private final boolean unmodifiable;
	// 第一步
	public ApplicationConversionService() {
		this(null);
	}
	
	// 第二步,StringValueResolver 为null
	public ApplicationConversionService(StringValueResolver embeddedValueResolver) {
		this(embeddedValueResolver, false);
	}

	// 第三步,StringValueResolver 为null,unmodifiable为false
	private ApplicationConversionService(StringValueResolver embeddedValueResolver, boolean unmodifiable) {
		if (embeddedValueResolver != null) {
			setEmbeddedValueResolver(embeddedValueResolver);
		}
		// 此处this就是ApplicationConversionService 
		configure(this);
		this.unmodifiable = unmodifiable;
	}
	
	// ApplicationConversionService extends FormattingConversionService
	// 而FormattingConversionService implements FormatterRegistry
	public static void configure(FormatterRegistry registry) {
		// 注册默认转换器组件
		DefaultConversionService.addDefaultConverters(registry);
		// 注册默认格式化器组件
		DefaultFormattingConversionService.addDefaultFormatters(registry);
		// 注册应用程序默认格式化器组件
		addApplicationFormatters(registry);
		// 注册应用程序默认转换器组件
		addApplicationConverters(registry);
	}
}

  configure(this)这里的 this 就是ApplicationConversionService,而ApplicationConversionService是继承FormattingConversionService 的,FormattingConversionService 又是实现FormatterRegistry的。此方法主要做了四件事:

  • 注册默认转换器组件
  • 注册默认格式化器组件
  • 注册应用程序默认格式化器组件
  • 注册应用程序默认转换器组件
2.3.1.2、注册默认的转换器

  此方法所在类的具体路径:org.springframework.core.convert.support.DefaultConversionService,简单了解即可。

	public static void addDefaultConverters(ConverterRegistry converterRegistry) {
		addScalarConverters(converterRegistry);
		addCollectionConverters(converterRegistry);

		converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
		converterRegistry.addConverter(new StringToTimeZoneConverter());
		converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
		converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());

		converterRegistry.addConverter(new ObjectToObjectConverter());
		converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
		converterRegistry.addConverter(new FallbackObjectToStringConverter());
		converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
	}

	public static void addCollectionConverters(ConverterRegistry converterRegistry) {
		ConversionService conversionService = (ConversionService) converterRegistry;

		converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
		converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));

		converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
		converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
		converterRegistry.addConverter(new MapToMapConverter(conversionService));

		converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
		converterRegistry.addConverter(new StringToArrayConverter(conversionService));

		converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
		converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));

		converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
		converterRegistry.addConverter(new StringToCollectionConverter(conversionService));

		converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
		converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));

		converterRegistry.addConverter(new StreamConverter(conversionService));
	}

	private static void addScalarConverters(ConverterRegistry converterRegistry) {
		converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());

		converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
		converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCharacterConverter());
		converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new NumberToCharacterConverter());
		converterRegistry.addConverterFactory(new CharacterToNumberFactory());

		converterRegistry.addConverter(new StringToBooleanConverter());
		converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
		converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));

		converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
		converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));

		converterRegistry.addConverter(new StringToLocaleConverter());
		converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCharsetConverter());
		converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCurrencyConverter());
		converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToPropertiesConverter());
		converterRegistry.addConverter(new PropertiesToStringConverter());

		converterRegistry.addConverter(new StringToUUIDConverter());
		converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
	}
2.3.1.3、注册默认格式化器组件

  此方法所在类的具体路径:org.springframework.format.support.DefaultFormattingConversionService,简单了解即可。

	public static void addDefaultFormatters(FormatterRegistry formatterRegistry) {
		// Default handling of number values
		formatterRegistry.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());

		// Default handling of monetary values
		if (jsr354Present) {
			formatterRegistry.addFormatter(new CurrencyUnitFormatter());
			formatterRegistry.addFormatter(new MonetaryAmountFormatter());
			formatterRegistry.addFormatterForFieldAnnotation(new Jsr354NumberFormatAnnotationFormatterFactory());
		}

		// Default handling of date-time values
		// just handling JSR-310 specific date and time types
		new DateTimeFormatterRegistrar().registerFormatters(formatterRegistry);

		if (jodaTimePresent) {
			// handles Joda-specific types as well as Date, Calendar, Long
			new org.springframework.format.datetime.joda.JodaTimeFormatterRegistrar().registerFormatters(formatterRegistry);
		}
		else {
			// regular DateFormat-based Date, Calendar, Long converters
			new DateFormatterRegistrar().registerFormatters(formatterRegistry);
		}
	}
2.3.1.4、注册应用程序默认格式化器组件

  此方法所在类的具体路径:org.springframework.boot.convert.ApplicationConversionService,简单了解即可。

	public static void addApplicationFormatters(FormatterRegistry registry) {
		registry.addFormatter(new CharArrayFormatter());
		registry.addFormatter(new InetAddressFormatter());
		registry.addFormatter(new IsoOffsetFormatter());
	}
2.3.1.5、注册应用程序默认转换器组件

  此方法所在类的具体路径:org.springframework.boot.convert.ApplicationConversionService,简单了解即可。

	public static void addApplicationConverters(ConverterRegistry registry) {
		addDelimitedStringConverters(registry);
		registry.addConverter(new StringToDurationConverter());
		registry.addConverter(new DurationToStringConverter());
		registry.addConverter(new NumberToDurationConverter());
		registry.addConverter(new DurationToNumberConverter());
		registry.addConverter(new StringToPeriodConverter());
		registry.addConverter(new PeriodToStringConverter());
		registry.addConverter(new NumberToPeriodConverter());
		registry.addConverter(new StringToDataSizeConverter());
		registry.addConverter(new NumberToDataSizeConverter());
		registry.addConverter(new StringToFileConverter());
		registry.addConverter(new InputStreamSourceToByteArrayConverter());
		registry.addConverterFactory(new LenientStringToEnumConverterFactory());
		registry.addConverterFactory(new LenientBooleanToEnumConverterFactory());
		if (registry instanceof ConversionService) {
			addApplicationConverters(registry, (ConversionService) registry);
		}
	}

	public static void addDelimitedStringConverters(ConverterRegistry registry) {
		ConversionService service = (ConversionService) registry;
		registry.addConverter(new ArrayToDelimitedStringConverter(service));
		registry.addConverter(new CollectionToDelimitedStringConverter(service));
		registry.addConverter(new DelimitedStringToArrayConverter(service));
		registry.addConverter(new DelimitedStringToCollectionConverter(service));
	}
2.3.2、配置propertySources

  这个方法主要处理两个事,默认配置和命令行配置(如果有)。

	protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
		// 获取可变属性源
		MutablePropertySources sources = environment.getPropertySources();
		// this.defaultProperties为null
		if (!CollectionUtils.isEmpty(this.defaultProperties)) {
			DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);
		}
		// 如果存在命令行参数,则解析它并封装进SimpleCommandLinePropertySource对象
		// 同时将此对象放到MutablePropertySources的第一个位置(优先级最高)
		// 我们上一篇文章是带有命令行参数的(--server.port=9000 --server.servlet.context-path=/spring-args Alian CSDN)
		if (this.addCommandLineProperties && args.length > 0) {
			// name的值就是"commandLineArgs"
			String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
			// 创建环境的时候只有四个值["servletConfigInitParams","servletContextInitParams","systemProperties","systemEnvironment"]
			// 是不存在"commandLineArgs"的
			if (sources.contains(name)) {
				PropertySource<?> source = sources.get(name);
				// 封装进SimpleCommandLinePropertySource对象,key为springApplicationCommandLineArgs,value为命令行参数
				CompositePropertySource composite = new CompositePropertySource(name);
				composite.addPropertySource(
						new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
				// name为commandLineArgs,value是SimpleCommandLinePropertySource
				composite.addPropertySource(source);
				// 替换的方式,不会破坏优先级顺序
				sources.replace(name, composite);
			} else {
				// 将其放到第一位置
				sources.addFirst(new SimpleCommandLinePropertySource(args));
			}
		}
	}
  • 默认配置:使用DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources)来配置
  • 命令行配置:如果有命令行参数则新增封装命令行参数的PropertySource,并将它放到 MutablePropertySources 的第一位置

  这里的主要作用是把命令行参数封装进SimpleCommandLinePropertySource,然后把它加到MutablePropertySources 列表的第一个位置,name是"commandLineArgs"

2.3.3、配置profiles

  本版本里configureProfiles是一个空方法,什么都不做。

	protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
	}

到这步配置环境后的结果:

ApplicationServletEnvironment {
	activeProfiles=[], 
	defaultProfiles=[default], 
	propertySources=[
		SimpleCommandLinePropertySource {name='commandLineArgs'}, 
		StubPropertySource {name='servletConfigInitParams'},
		StubPropertySource {name='servletContextInitParams'}, 
		PropertiesPropertySource {name='systemProperties'}, 
		SystemEnvironmentPropertySource {name='systemEnvironment'}
	]
}

  相比之前就是新增了命令行参数的处理:SimpleCommandLinePropertySource {name=‘commandLineArgs’}

2.4、附加指定的环境配置

  此方法所在类的具体路径:org.springframework.boot.context.properties.source.ConfigurationPropertySources

	private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties";
	
	public static void attach(Environment environment) {
		// 判断environment是否是ConfigurableEnvironment的实例,从我们之前创建环境的类图就知道是了
		Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
		// 从environment获取PropertySources,从配置环境我们知道现在有5个结果(前提是启动时加入了命令行参数,不然就是4个)
		MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
		// 获取name为configurationProperties的sources
		// ["commandLineArgs","servletConfigInitParams","servletContextInitParams","systemProperties","systemEnvironment"]
		// 此时很明显没有,则attached 为空
		PropertySource<?> attached = getAttached(sources);
		if (attached != null && attached.getSource() != sources) {
			sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
			attached = null;
		}
		if (attached == null) {
			// 将sources封装成ConfigurationPropertySourcesPropertySource对象,name为configurationProperties
			// 并把这个对象放到sources的第一位置
			sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
					new SpringConfigurationPropertySources(sources)));
		}
	}

	static PropertySource<?> getAttached(MutablePropertySources sources) {
		return (sources != null) ? sources.get(ATTACHED_PROPERTY_SOURCE_NAME) : null;
	}

实际上做完这一步的结果如下:

ApplicationServletEnvironment {
	activeProfiles=[], 
	defaultProfiles=[default], 
	propertySources=[
		ConfigurationPropertySourcesPropertySource {name='configurationProperties'},
		SimpleCommandLinePropertySource {name='commandLineArgs'}, 
		StubPropertySource {name='servletConfigInitParams'}, 
		StubPropertySource {name='servletContextInitParams'}, 
		PropertiesPropertySource {name='systemProperties'}, 
		SystemEnvironmentPropertySource {name='systemEnvironment'}
	]
}

  相比之前环境配置新增了:ConfigurationPropertySourcesPropertySource {name=‘configurationProperties’}

2.5、发布环境准备事件

  说到这里,实际上只要看我之前文章,应该对流程会非常的熟悉。不管总样,我们再简单过一下流程。

2.5.1、遍历运行监听器

此方法所在类的具体路径:org.springframework.boot.SpringApplicationRunListeners

class SpringApplicationRunListeners {

	private final List<SpringApplicationRunListener> listeners;
	
	void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
		doWithListeners("spring.boot.application.environment-prepared",
				(listener) -> listener.environmentPrepared(bootstrapContext, environment));
	}

	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
		doWithListeners(stepName, listenerAction, null);
	}

	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
			Consumer<StartupStep> stepAction) {
		StartupStep step = this.applicationStartup.start(stepName);
		this.listeners.forEach(listenerAction);
		if (stepAction != null) {
			stepAction.accept(step);
		}
		step.end();
	}
}

  之前的文章就知道了,这里的运行监听器就一个,那就是EventPublishingRunListener,接下里就是去看看事件发布运行监听器的环境准备事件做了什么。

2.5.2、事件发布运行监听器

  此方法所在类的具体路径:org.springframework.boot.context.event.EventPublishingRunListener

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SimpleApplicationEventMulticaster initialMulticaster;
	
	@Override
	public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
			ConfigurableEnvironment environment) {
		// 交给事件广播器处理
		this.initialMulticaster.multicastEvent(
				// 实例化ApplicationEnvironmentPreparedEvent
				new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
	}
}
  • 实例化ApplicationEnvironmentPreparedEvent
  • 通过事件广播器发布事件
2.5.3、事件广播器

  此方法所在类的具体路径:org.springframework.context.event.SimpleApplicationEventMulticaster

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

	@Override
	public void multicastEvent(ApplicationEvent event) {
		// 通过事件解析事件类型
		// 广播事件
		multicastEvent(event, resolveDefaultEventType(event));
	}

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		// executor默认为null
		Executor executor = getTaskExecutor();
		// 根据事件和事件类型获取监听器列表
		// 然后遍历监听器列表,分别调用监听器的方法
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			} else {
				// 调用
				invokeListener(listener, event);
			}
		}
	}
	
}
  • 首先根据事件解析事件的类型(这里的事件是ApplicationEnvironmentPreparedEvent
  • 根据事件和事件类型进行事件广播
  • 根据事件和事件类型获取事件监听器列表(重点
2.5.4、获取符合事件的监听器

  这里调用getApplicationListeners(event, type)方法,实际就是调用抽象类AbstractApplicationEventMulticaster中的getApplicationListeners方法。

	protected Collection<ApplicationListener<?>> getApplicationListeners(
			ApplicationEvent event, ResolvableType eventType) {
		// event为ApplicationEnvironmentPreparedEvent
		// event里面的source就是SpringApplication,是在实例化ApplicationEnvironmentPreparedEvent时赋值的
		Object source = event.getSource();
		Class<?> sourceType = (source != null ? source.getClass() : null);
		
		// 静态内部类,也就是根据 event 类型和 source 类型构造出来的对象
		ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
		
		// 要填充的CachedListenerRetriever 
		CachedListenerRetriever newRetriever = null;

		// 此时是为null,retrieverCache在初始化应用程序事件广播器就初始化好了
		// retrieverCache用于存储监听器,变量的定义如下,它的键是 ListenerCacheKey,值是 CachedListenerRetriever
		CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
		if (existingRetriever == null) {
			// Caching a new ListenerRetriever if possible
			if (this.beanClassLoader == null || (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
							(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
				// 初始化CachedListenerRetriever
				newRetriever = new CachedListenerRetriever();
				// putIfAbsent   如果传入key对应的value已经存在,就返回存在的value,不进行替换。
				// 如果不存在,就添加key和value,返回null
				// 此处不存在,返回null
				existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
				if (existingRetriever != null) {
					newRetriever = null;  // no need to populate it in retrieveApplicationListeners
				}
			}
		}
		// 此时existingRetriever 为null
		if (existingRetriever != null) {
			Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
			if (result != null) {
				return result;
			}
		}
		return retrieveApplicationListeners(eventType, sourceType, newRetriever);
	}
  • event里面的source就是SpringApplication,是在实例化ApplicationStartingEvent时赋值的
  • ListenerCacheKey 根据 event 类型和 source 类型构造出来的对象
  • retrieverCache 用于存储监听器,它的键是 ListenerCacheKey,值是 CachedListenerRetriever
  • existingRetriever 是已缓存的监听器

我们继续跟进retrieveApplicationListeners方法

	private Collection<ApplicationListener<?>> retrieveApplicationListeners(
			ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {

		List<ApplicationListener<?>> allListeners = new ArrayList<>();
		Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
		Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);

		Set<ApplicationListener<?>> listeners;
		Set<String> listenerBeans;
		synchronized (this.defaultRetriever) {
			// this.defaultRetriever.applicationListeners的值是通过实例化EventPublishingRunListener时通过构造方法加入的
			listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
			// listenerBeans是为空
			listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
		}

		// 从实例化EventPublishingRunListener我们知道加入到广播器的一共有8个
		for (ApplicationListener<?> listener : listeners) {
			// 根据event类型source类型对listener进行匹配判断
			if (supportsEvent(listener, eventType, sourceType)) {
				if (retriever != null) {
					filteredListeners.add(listener);
				}
				// 匹配上了就加入列表
				allListeners.add(listener);
			}
		}

		// listenerBeans为空
		if (!listenerBeans.isEmpty()) {
			// 不执行省略代码
		}
		// 匹配后的监听器排序
		// 最终allListeners结果是6个:
		// EnvironmentPostProcessorApplicationListener、AnsiOutputApplicationListener、LoggingApplicationListener
		// BackgroundPreinitializer、DelegatingApplicationListener、FileEncodingApplicationListener
		AnnotationAwareOrderComparator.sort(allListeners);
		// retriever此时有ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent
		if (retriever != null) {
			if (filteredListenerBeans.isEmpty()) {
				// 把匹配的监听器存储下来(为6个)
				retriever.applicationListeners = new LinkedHashSet<>(allListeners);
				retriever.applicationListenerBeans = filteredListenerBeans;
			} else {
				retriever.applicationListeners = filteredListeners;
				retriever.applicationListenerBeans = filteredListenerBeans;
			}
		}
		// 返回结果
		return allListeners;
	}

  最终匹配到的监听器结果:

  • EnvironmentPostProcessorApplicationListener
  • AnsiOutputApplicationListener
  • LoggingApplicationListener
  • BackgroundPreinitializer
  • DelegatingApplicationListener
  • FileEncodingApplicationListener

也符合我们下面这个对照表:

监听器对应的事件
AnsiOutputApplicationListenerApplicationEnvironmentPreparedEvent
BackgroundPreinitializerSpringApplicationEvent
ClearCachesApplicationListenerContextRefreshedEvent
DelegatingApplicationListenerApplicationEvent
EnvironmentPostProcessorApplicationListenerApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ApplicationFailedEvent
FileEncodingApplicationListenerApplicationEnvironmentPreparedEvent
LoggingApplicationListenerApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、 ContextClosedEvent、ApplicationFailedEvent
ParentContextCloserApplicationListenerParentContextAvailableEvent

  其中ApplicationContextInitializedEvent、ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、 ApplicationReadyEvent、ApplicationStartedEvent、ApplicationFailedEvent 这7个是继承SpringApplicationEvent,而 SpringApplicationEvent 继承 ApplicationEvent,属性源都是SpringApplication时,满足自身事件或父类的都会匹配上。

2.5.5、监听器执行

  方法所在类的具体路径:org.springframework.context.event.SimpleApplicationEventMulticaster

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

	// 使用给定的事件调用给定的监听器
	protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		// ErrorHandler 默认为null
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
				doInvokeListener(listener, event);
			} catch (Throwable err) {
				errorHandler.handleError(err);
			}
		} else {
			// 调用
			doInvokeListener(listener, event);
		}
	}

	@SuppressWarnings({"rawtypes", "unchecked"})
	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			// 调用监听器的onApplicationEvent方法
			listener.onApplicationEvent(event);
		} catch (ClassCastException ex) {
			String msg = ex.getMessage();
			if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
					(event instanceof PayloadApplicationEvent &&
							matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
				// Possibly a lambda-defined listener which we could not resolve the generic event type for
				// -> let's suppress the exception.
				Log loggerToUse = this.lazyLogger;
				if (loggerToUse == null) {
					loggerToUse = LogFactory.getLog(getClass());
					this.lazyLogger = loggerToUse;
				}
				if (loggerToUse.isTraceEnabled()) {
					loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
				}
			} else {
				throw ex;
			}
		}
	}
}
  • 遍历监听器列表,执行 invokeListener(listener, event)
  • doInvokeListener(listener, event)
  • 调用监听器的onApplicationEvent(event)方法
2.5.6、配置文件加载

  我们在上一小节就说到了只有6个符合条件的监听器,它们都会执行它们的onApplicationEvent,这里就只讲一个大家比较关心的监听器:EnvironmentPostProcessorApplicationListener,就是它完成了配置文件的读取,我这个是新的版本哦,老版本里的监听器是ConfigFileApplicationListener。编辑器一直提醒我篇幅太长,我只能在下一篇单独讲解了。

运行完的结果(省略值):

ApplicationServletEnvironment {
	activeProfiles=[], 
	defaultProfiles=[default], 
	propertySources=[
		ConfigurationPropertySourcesPropertySource {name='configurationProperties'},
		SimpleCommandLinePropertySource {name='commandLineArgs'}, 
		StubPropertySource {name='servletConfigInitParams'}, 
		StubPropertySource {name='servletContextInitParams'}, 
		PropertiesPropertySource {name='systemProperties'}, 
		OriginAwareSystemEnvironmentPropertySource@1483145520 {name='systemEnvironment'},
		RandomValuePropertySource@1961129028 {name='random'},
		OriginTrackedMapPropertySource@1506938939 {name='Config resource 'class path resource [application.yml]' via location 'optional:classpath:/'', }
	]
}

  相比之前环境配置新增了:RandomValuePropertySource OriginTrackedMapPropertySource,当然也把第6个替换为OriginAwareSystemEnvironmentPropertySource@1483145520 {name=‘systemEnvironment’}

2.6、环境配置绑定

  方法所在类的具体路径:org.springframework.boot.DefaultPropertiesPropertySource

	public static final String NAME = "defaultProperties";
	
	public static void moveToEnd(ConfigurableEnvironment environment) {
		moveToEnd(environment.getPropertySources());
	}

	public static void moveToEnd(MutablePropertySources propertySources) {
		// 判断defaultProperties是否存在
		PropertySource<?> propertySource = propertySources.remove(NAME);
		if (propertySource != null) {
			// 存在则加入到MutablePropertySources列表最后
			propertySources.addLast(propertySource);
		}
	}
	protected void bindToSpringApplication(ConfigurableEnvironment environment) {
		try {
			Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
		}
		catch (Exception ex) {
			throw new IllegalStateException("Cannot bind to SpringApplication", ex);
		}
	}

  SpringApplication中的sources对象的值就变了,如下:
在这里插入图片描述
  如果你要达到我这种效果需要在配置文件中加入如下配置:

spring:
  main:
    sources: com.alian.springboot

2.7、环境转换

  继续解读接下里的代码:

	if (!this.isCustomEnvironment) {
		// 根据spring.main.web-application-type配置判断是否需要转换环境
		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
				deduceEnvironmentClass());
	}

  方法所在类的具体路径:org.springframework.boot.EnvironmentConverter ,上面的章节有把类图贴出来。

	private Class<? extends StandardEnvironment> deduceEnvironmentClass() {
		switch (this.webApplicationType) {
		case SERVLET:
			// SERVLET 环境
			return ApplicationServletEnvironment.class;
		case REACTIVE:
			// 响应式 WEB 环境
			return ApplicationReactiveWebEnvironment.class;
		default:
			// 标准环境
			return ApplicationEnvironment.class;
		}
	}

  convertEnvironmentIfNecessary方法所在类的具体路径:org.springframework.boot.EnvironmentConverter ,此类比较简单就过多解释了。

package org.springframework.boot;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.support.StandardServletEnvironment;

final class EnvironmentConverter {

	private static final String CONFIGURABLE_WEB_ENVIRONMENT_CLASS = "org.springframework.web.context.ConfigurableWebEnvironment";

	private static final Set<String> SERVLET_ENVIRONMENT_SOURCE_NAMES;

	static {
		Set<String> names = new HashSet<>();
		names.add(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME);
		names.add(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME);
		names.add(StandardServletEnvironment.JNDI_PROPERTY_SOURCE_NAME);
		SERVLET_ENVIRONMENT_SOURCE_NAMES = Collections.unmodifiableSet(names);
	}

	private final ClassLoader classLoader;

	EnvironmentConverter(ClassLoader classLoader) {
		this.classLoader = classLoader;
	}

	StandardEnvironment convertEnvironmentIfNecessary(ConfigurableEnvironment environment,
			Class<? extends StandardEnvironment> type) {
		if (type.equals(environment.getClass())) {
			return (StandardEnvironment) environment;
		}
		return convertEnvironment(environment, type);
	}

	private StandardEnvironment convertEnvironment(ConfigurableEnvironment environment,
			Class<? extends StandardEnvironment> type) {
		// 根据刚才获取的class:ApplicationServletEnvironment.class获取标准环境
		StandardEnvironment result = createEnvironment(type);
		result.setActiveProfiles(environment.getActiveProfiles());
		// 设置转换服务
		result.setConversionService(environment.getConversionService());
		// 拷贝属性源(包括容器及环境配置)
		copyPropertySources(environment, result);
		return result;
	}

	private StandardEnvironment createEnvironment(Class<? extends StandardEnvironment> type) {
		try {
			return type.getDeclaredConstructor().newInstance();
		}
		catch (Exception ex) {
			return new StandardEnvironment();
		}
	}

	private void copyPropertySources(ConfigurableEnvironment source, StandardEnvironment target) {
		removePropertySources(target.getPropertySources(), isServletEnvironment(target.getClass(), this.classLoader));
		for (PropertySource<?> propertySource : source.getPropertySources()) {
			if (!SERVLET_ENVIRONMENT_SOURCE_NAMES.contains(propertySource.getName())) {
				target.getPropertySources().addLast(propertySource);
			}
		}
	}

	private boolean isServletEnvironment(Class<?> conversionType, ClassLoader classLoader) {
		try {
			Class<?> webEnvironmentClass = ClassUtils.forName(CONFIGURABLE_WEB_ENVIRONMENT_CLASS, classLoader);
			return webEnvironmentClass.isAssignableFrom(conversionType);
		}
		catch (Throwable ex) {
			return false;
		}
	}

	private void removePropertySources(MutablePropertySources propertySources, boolean isServletEnvironment) {
		Set<String> names = new HashSet<>();
		for (PropertySource<?> propertySource : propertySources) {
			names.add(propertySource.getName());
		}
		for (String name : names) {
			if (!isServletEnvironment || !SERVLET_ENVIRONMENT_SOURCE_NAMES.contains(name)) {
				propertySources.remove(name);
			}
		}
	}

}

2.8、解除附加环境配置

  此方法所在类的具体路径:org.springframework.boot.context.properties.source.ConfigurationPropertySources

	private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties";
	
	public static void attach(Environment environment) {
		// 判断environment是否是ConfigurableEnvironment的实例,从我们之前创建环境的类图就知道是了
		Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
		// 从environment获取PropertySources,从配置环境我们知道现在有8个结果
		// 前提是只有一个配置文件application.yml,启动时加入了命令行参数
		// 多个配置文件会有多个结果,比如有application.yml 和 application.Properties,就比一个配置文件的多一个结果
		MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
		// 获取name为configurationProperties的sources
		// ["configurationProperties","commandLineArgs","servletConfigInitParams","servletContextInitParams","systemProperties","systemEnvironment","random","Config resource 'class path resource [application.yml]' via location 'optional:classpath:/'"]
		// 很明显是存在的
		PropertySource<?> attached = getAttached(sources);
		if (attached != null && attached.getSource() != sources) {
			// 先移除name为configurationProperties的配置
			sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
			attached = null;
		}
		if (attached == null) {
			// 再次将sources封装成ConfigurationPropertySourcesPropertySource对象,name为configurationProperties
			// 并把这个对象放到sources的第一位置
			sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
					new SpringConfigurationPropertySources(sources)));
		}
	}

	static PropertySource<?> getAttached(MutablePropertySources sources) {
		return (sources != null) ? sources.get(ATTACHED_PROPERTY_SOURCE_NAME) : null;
	}

  先移除name为configurationProperties的配置,再封装一个ConfigurationPropertySourcesPropertySource对象,name为configurationProperties,并把这个对象放在第一位。

三、配置忽略的bean信息

  得到系统属性spring.beaninfo.ignore,如果为空设置为true,后续获取bean进行类型判断会用到。

	private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
		if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
			Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
			System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
		}
	}

结语

  本文解读了最新版springboot应用环境准备工作,里面最主要的流程都比较的清晰,但是也比较重要,但是监听器的执行我们还没有讲解,下一篇解读启动流程分析之应用环境准备(加载配置文件),看看配置文件怎么加载的。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值