目录
一、背景
上一篇我们解读了命令行参数解析,本篇主要解读应用环境准备,老样子还是回顾下启动的整体流程,这样就能不迷路。
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
也符合我们下面这个对照表:
监听器 | 对应的事件 |
---|---|
AnsiOutputApplicationListener | ApplicationEnvironmentPreparedEvent |
BackgroundPreinitializer | SpringApplicationEvent |
ClearCachesApplicationListener | ContextRefreshedEvent |
DelegatingApplicationListener | ApplicationEvent |
EnvironmentPostProcessorApplicationListener | ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、ApplicationFailedEvent |
FileEncodingApplicationListener | ApplicationEnvironmentPreparedEvent |
LoggingApplicationListener | ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationPreparedEvent、 ContextClosedEvent、ApplicationFailedEvent |
ParentContextCloserApplicationListener | ParentContextAvailableEvent |
其中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应用环境准备工作,里面最主要的流程都比较的清晰,但是也比较重要,但是监听器的执行我们还没有讲解,下一篇解读启动流程分析之应用环境准备(加载配置文件),看看配置文件怎么加载的。