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

Alian解读SpringBoot 2.6.0 源码(四):启动流程分析之应用环境准备一 背景 1 1 run 方法整体流程 1 2 本文解读范围二 应用环境准备 2 1 准备环境的整体流程 2 2 创建环境 2 3 配置环境 2 4 附加指定的环境配置 2 5 发布环境准备事件 2 6 环境配置绑

大家好,欢迎来到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

(0)
上一篇 2025-06-25 12:10
下一篇 2025-06-25 12:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信