大家好,欢迎来到IT知识分享网。
目录
一、背景
上一篇我们解读了命令行参数解析,本篇主要解读应用环境准备,老样子还是回顾下启动的整体流程,这样就能不迷路。
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@ {name='systemEnvironment'}, RandomValuePropertySource@ {name='random'}, OriginTrackedMapPropertySource@ {name='Config resource 'class path resource [application.yml]' via location 'optional:classpath:/'', } ] }
相比之前环境配置新增了:RandomValuePropertySource 和 OriginTrackedMapPropertySource,当然也把第6个替换为OriginAwareSystemEnvironmentPropertySource@ {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); } }
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应用环境准备工作,里面最主要的流程都比较的清晰,但是也比较重要,但是监听器的执行我们还没有讲解,下一篇解读启动流程分析之应用环境准备(加载配置文件),看看配置文件怎么加载的。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/136784.html