网站后台数据,宜昌最新消息今天,网站为何不显示百度商桥对话框,wordpress 更新慢SpringBoot源码系列文章
SpringBoot源码解析(一)#xff1a;SpringApplication构造方法
SpringBoot源码解析(二)#xff1a;引导上下文DefaultBootstrapContext
SpringBoot源码解析(三)#xff1a;启动开始阶段
SpringBoot源码解析(四)#xff1a;解析应用参数args
Sp…SpringBoot源码系列文章
SpringBoot源码解析(一)SpringApplication构造方法
SpringBoot源码解析(二)引导上下文DefaultBootstrapContext
SpringBoot源码解析(三)启动开始阶段
SpringBoot源码解析(四)解析应用参数args
SpringBoot源码解析(五)准备应用环境 目录 前言一、入口二、环境实例ApplicationServletEnvironment1、PropertyResolver2、Environment3、ConfigurablePropertyResolver4、ConfigurableEnvironment5、ConfigurableWebEnvironment6、AbstractEnvironment7、StandardEnvironment8、StandardServletEnvironment9、ApplicationServletEnvironment 三、配置环境1、命令行参数属性源2、配置属性源 四、触发的应用监听器1、EnvironmentPostProcessorApplicationListener1.1、RandomValuePropertySourceEnvironmentPostProcessor1.2、SystemEnvironmentPropertySourceEnvironmentPostProcessor1.3、SpringApplicationJsonEnvironmentPostProcessor1.4、ConfigDataEnvironmentPostProcessor 2、AnsiOutputApplicationListener3、BackgroundPreinitializer4、FileEncodingApplicationListener 五、默认属性源六、绑定spring.main配置到SpringApplication对象总结 前言 在前文中我们深入解析了启动类main函数中args参数被解析为选项参数和非选项参数的过程。接下来我们将探讨SpringBoot启动时应用环境的准备过程包括读取配置文件和设置环境变量的步骤。 SpringBoot版本2.7.18SpringApplication的run方法的执行逻辑如下本文将详细介绍第4小节准备应用环境 // SpringApplication类方法
public ConfigurableApplicationContext run(String... args) {// 记录应用启动的开始时间long startTime System.nanoTime();// 1.创建引导上下文用于管理应用启动时的依赖和资源DefaultBootstrapContext bootstrapContext createBootstrapContext();ConfigurableApplicationContext context null;// 配置无头模式属性以支持在无图形环境下运行// 将系统属性 java.awt.headless 设置为 trueconfigureHeadlessProperty();// 2.获取Spring应用启动监听器用于在应用启动的各个阶段执行自定义逻辑SpringApplicationRunListeners listeners getRunListeners(args);// 启动开始方法发布开始事件、通知应用监听器ApplicationListenerlisteners.starting(bootstrapContext, this.mainApplicationClass);try {// 3.解析应用参数ApplicationArguments applicationArguments new DefaultApplicationArguments(args);// 4.准备应用环境包括读取配置文件和设置环境变量ConfigurableEnvironment environment prepareEnvironment(listeners, bootstrapContext, applicationArguments);// 配置是否忽略 BeanInfo以加快启动速度configureIgnoreBeanInfo(environment);// 5.打印启动BannerBanner printedBanner printBanner(environment);// 6.创建应用程序上下文context createApplicationContext();// 设置应用启动的上下文用于监控和管理启动过程context.setApplicationStartup(this.applicationStartup);// 7.准备应用上下文包括加载配置、添加 Bean 等prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 8.刷新上下文完成 Bean 的加载和依赖注入refreshContext(context);// 9.刷新后的一些操作如事件发布等afterRefresh(context, applicationArguments);// 计算启动应用程序的时间并记录日志Duration timeTakenToStartup Duration.ofNanos(System.nanoTime() - startTime);if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);}// 10.通知监听器应用启动完成listeners.started(context, timeTakenToStartup);// 11.调用应用程序中的 CommandLineRunner 或 ApplicationRunner以便执行自定义的启动逻辑callRunners(context, applicationArguments);}catch (Throwable ex) {// 12.处理启动过程中发生的异常并通知监听器handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {// 13.计算应用启动完成至准备就绪的时间并通知监听器Duration timeTakenToReady Duration.ofNanos(System.nanoTime() - startTime);listeners.ready(context, timeTakenToReady);}catch (Throwable ex) {// 处理准备就绪过程中发生的异常handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}// 返回已启动并准备就绪的应用上下文return context;
}一、入口
// 4.准备应用环境包括读取配置文件和设置环境变量
ConfigurableEnvironment environment prepareEnvironment(listeners, bootstrapContext, applicationArguments);初始化并配置Spring应用环境
// SpringApplication类方法
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {// 4.1.获取环境实例 ApplicationServletEnvironmentConfigurableEnvironment environment getOrCreateEnvironment();// 4.2.配置环境// 将命令行参数传递给环境配置configureEnvironment(environment, applicationArguments.getSourceArgs());// 添加配置属性源到环境中使其能够解析相关配置属性ConfigurationPropertySources.attach(environment);// 4.3.通知监听器环境已准备好listeners.environmentPrepared(bootstrapContext, environment);// 4.4.将默认属性源移到环境属性源列表的末尾DefaultPropertiesPropertySource.moveToEnd(environment);// spring.main.environment-prefix是SpringBoot用于管理上下文环境的一部分// 不应该由用户直接修改如果被用户定义则抛错Assert.state(!environment.containsProperty(spring.main.environment-prefix),Environment prefix cannot be set via properties.);// 4.5.绑定spring.main环境配置到SpringApplication对象上bindToSpringApplication(environment);// 如果环境不是自定义的则进行环境转换确定必要的环境类型if (!this.isCustomEnvironment) {EnvironmentConverter environmentConverter new EnvironmentConverter(getClassLoader());environment environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());}// 再次设置配置属性源确保配置属性源在第一位ConfigurationPropertySources.attach(environment);// 返回配置完成的环境对象return environment;
}二、环境实例ApplicationServletEnvironment getOrCreateEnvironment()方法的核心是创建一个 ApplicationServletEnvironment实例下面将重点研究该类。
先看类图从上到下逐一分析 1、PropertyResolver PropertyResolver是Spring核心框架中的一个接口提供了解析属性值的统一方法。它支持从多种配置源如系统属性、环境变量、配置文件等获取属性值广泛用于环境配置、占位符解析等场景。
属性检查 判断某个属性是否存在方法containsProperty(String key) 获取属性值 获取指定键的属性值支持默认值、类型转换等方法getProperty(String key)、getProperty(String key, String defaultValue)、getProperty(String key, Class targetType) 等 必需属性值 获取指定键的属性值若找不到则抛出异常方法getRequiredProperty(String key)、getRequiredProperty(String key, Class targetType) 占位符解析 解析字符串中的 ${…} 占位符替换为对应的属性值方法resolvePlaceholders(String text)、resolveRequiredPlaceholders(String text)
// 用于解析属性值的接口支持从底层源解析属性
public interface PropertyResolver {// 判断指定的属性键是否可用即该键对应的值是否不为nullboolean containsProperty(String key);// 返回与指定键关联的属性值如果未找到则返回nullNullableString getProperty(String key);// 返回与指定键关联的属性值如果未找到则返回默认值String getProperty(String key, String defaultValue);// 返回与指定键关联的属性值并将其转换为指定的目标类型如果未找到则返回nullNullableT T getProperty(String key, ClassT targetType);// 返回与指定键关联的属性值并将其转换为目标类型如果未找到则返回默认值T T getProperty(String key, ClassT targetType, T defaultValue);// 返回与指定键关联的属性值不能为nullString getRequiredProperty(String key) throws IllegalStateException;// 返回与指定键关联的属性值并将其转换为目标类型不能为nullT T getRequiredProperty(String key, ClassT targetType) throws IllegalStateException;// 解析给定文本中的 ${...} 占位符并用对应的属性值替换。// 未解析的占位符会被忽略并原样返回。String resolvePlaceholders(String text);// 解析给定文本中的 ${...} 占位符并用对应的属性值替换// 未解析的占位符将抛出 IllegalArgumentException 异常String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}2、Environment Environment是Spring框架中的一个核心接口用于表示应用程序的运行环境。它扩展了PropertyResolver接口既负责属性解析也负责Profile管理。在Spring中它的主要用途是管理配置文件Profiles和属性Properties。
// 表示当前应用程序运行环境的接口
public interface Environment extends PropertyResolver {// 返回当前环境中激活的ProfilesString[] getActiveProfiles();// 返回当前环境中默认激活的 ProfilesString[] getDefaultProfiles();// 检查给定的 Profile 表达式是否与当前激活的 Profiles 匹配default boolean matchesProfiles(String... profileExpressions) {return acceptsProfiles(Profiles.of(profileExpressions));}// 检查一个或多个给定的 Profiles 是否被当前环境接受Deprecatedboolean acceptsProfiles(String... profiles);// 检查给定的 Profiles 谓词是否与当前激活的或默认的 Profiles 匹配boolean acceptsProfiles(Profiles profiles);
}3、ConfigurablePropertyResolver ConfigurablePropertyResolver是Spring中PropertyResolver的扩展接口为属性解析器添加了额外的配置能力主要用于高级属性管理和占位符解析。它允许自定义属性解析行为如类型转换服务、占位符格式以及验证必需的属性。
// 用于在将属性值从一种类型转换为另一种类型时使用
public interface ConfigurablePropertyResolver extends PropertyResolver {// 获取用于属性值类型转换的 ConfigurableConversionService 实例ConfigurableConversionService getConversionService();// 设置类型转换服务void setConversionService(ConfigurableConversionService conversionService);// 设置占位符的前缀默认为 ${// 示例若设置为 #{则占位符形如 #{property}void setPlaceholderPrefix(String placeholderPrefix);// 设置占位符的后缀默认为 }// 示例与 setPlaceholderPrefix 配合使用解析 #{property}void setPlaceholderSuffix(String placeholderSuffix);// 设置占位符和默认值之间的分隔符默认值为 :// 示例${property:defaultValue} 表示如果 property 未定义则返回 defaultValuevoid setValueSeparator(Nullable String valueSeparator);// 设置是否忽略无法解析的嵌套占位符// true保留未解析的占位符如 ${unresolved}// false遇到未解析的占位符时抛出异常void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);// 设置必须存在的属性void setRequiredProperties(String... requiredProperties);// 验证所有必需属性是否存在并解析为非 nullvoid validateRequiredProperties() throws MissingRequiredPropertiesException;
}4、ConfigurableEnvironment ConfigurableEnvironment是Spring框架中的一个核心接口扩展了Environment和ConfigurablePropertyResolver接口提供了对Profile和属性源PropertySources 的动态管理功能。它主要用于应用启动前的环境配置允许开发者根据需求自定义属性解析规则和Profile配置。
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {// 设置当前环境的 Active Profiles激活的配置void setActiveProfiles(String... profiles);// 向当前的 Active Profiles 集合中添加一个 Profilevoid addActiveProfile(String profile);// 设置默认激活的 Profile 集合void setDefaultProfiles(String... profiles);// 返回当前环境的属性源集合允许动态操作属性源MutablePropertySources getPropertySources();// 返回JVM的系统属性System.getProperties()MapString, Object getSystemProperties();// 返回操作系统的环境变量System.getenv()MapString, Object getSystemEnvironment();// 将父环境的 Active Profiles、Default Profiles 和 属性源 合并到当前环境中void merge(ConfigurableEnvironment parent);
}5、ConfigurableWebEnvironment ConfigurableWebEnvironment是Spring框架中ConfigurableEnvironment的特化接口主要用于基于Servlet环境的Web应用程序。它扩展了 ConfigurableEnvironment并增加了与Servlet相关的配置功能确保在ServletContext和ServletConfig可用时能够尽早初始化与 Servlet 环境相关的属性源。
public interface ConfigurableWebEnvironment extends ConfigurableEnvironment {// 使用给定的 ServletContext 和可选的ServletConfig 初始化属性源。// 该方法会将占位符性质的属性源替换为实际的 Servlet 上下文或配置属性源。// 调用时机Spring Boot 中的内嵌容器如 Tomcat、Jetty启动时void initPropertySources(Nullable ServletContext servletContext, Nullable ServletConfig servletConfig);
}6、AbstractEnvironment AbstractEnvironment作为Environment实现的基础类主要提供对配置文件Profiles 和属性源PropertySources的管理。它支持定义和管理激活配置文件Active Profiles 以及 默认配置文件Default Profiles。
public abstract class AbstractEnvironment implements ConfigurableEnvironment {// 激活配置文件集合private final SetString activeProfiles new LinkedHashSet();// 默认配置文件集合 getReservedDefaultProfiles() 字符串“default”private final SetString defaultProfiles new LinkedHashSet(getReservedDefaultProfiles());// 属性源集合上篇文章有介绍属性源就是一组属性map键值对这里集合就是多组键值对private final MutablePropertySources propertySources;// 属性解析器private final ConfigurablePropertyResolver propertyResolver;public AbstractEnvironment() {this(new MutablePropertySources());}protected AbstractEnvironment(MutablePropertySources propertySources) {this.propertySources propertySources;this.propertyResolver createPropertyResolver(propertySources);customizePropertySources(propertySources);}// 自定义属性源对属性源增删改查做操作protected void customizePropertySources(MutablePropertySources propertySources) {// 默认无操作子类可覆盖}// 省略其他方法
}PropertySource表示属性来源的抽象概念每个属性源PropertySource封装了一组键值对key-value并可以根据键解析属性值。用于从各种来源如系统属性、环境变量、配置文件等加载属性
public abstract class PropertySourceT {// 属性源名称唯一标识protected final String name;// 一般是map键值对protected final T source;...
}MutablePropertySources用于管理多个属性源PropertySource的集合。它提供了一个可变的数据结构允许开发者动态地添加、删除、替换或调整属性源的顺序。
addFirst(PropertySource? propertySource)将属性源添加到集合的首位优先级最高addLast(PropertySource? propertySource)将属性源添加到集合的末尾优先级最低addBefore(String relativePropertySourceName, PropertySource? propertySource)在指定名称的属性源之前插入addAfter(String relativePropertySourceName, PropertySource? propertySource)在指定名称的属性源之后插入
public class MutablePropertySources implements PropertySources {// 属性源集合private final ListPropertySource? propertySourceList new CopyOnWriteArrayList();...
}7、StandardEnvironment StandardEnvironment提供了对系统属性System Properties和环境变量Environment Variables的支持并在默认情况下将这两个属性源添加到 MutablePropertySources中。 AbstractEnvironment构造方法中会调用customizePropertySources方法也就是创建ApplicationServletEnvironment实例就是添加两个属性源名称为systemProperties为的JVM系统属性如 java.version、java.home 等和名称为systemEnvironment的环境变量PATH、HOME等操作系统相关变量。
public class StandardEnvironment extends AbstractEnvironment {// 系统环境变量属性源的名称public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME systemEnvironment;// JVM 系统属性源的名称public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME systemProperties;public StandardEnvironment() {}protected StandardEnvironment(MutablePropertySources propertySources) {super(propertySources);}// 自定义操作属性源父类构造中会调用Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {// addLast将属性放到最后优先级最低JVM优先级高于环境变量propertySources.addLast(// 通过 System.getProperties() 提供的 JVM 系统属性new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));propertySources.addLast(// 通过 System.getenv() 提供的环境变量new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));}
}8、StandardServletEnvironment StandardServletEnvironment是Spring框架中的一个类继承自StandardEnvironment用于为基于Servlet的Web应用程序提供专门的Environment实现。它扩展了标准环境StandardEnvironment的功能增加了对Servlet相关属性如ServletConfig和ServletContext的支持。 属性优先级ServletConfig ServletContext JNDI 系统属性 环境变量。 在应用上下文启动时StandardServletEnvironment会将占位符属性StubPropertySource替换为实际的ServletConfig和ServletContext属性源。
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment {// ServletContext 初始化参数属性源的名称public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME servletContextInitParams;// ServletConfig 初始化参数属性源的名称public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME servletConfigInitParams;// JNDI 属性源名称public static final String JNDI_PROPERTY_SOURCE_NAME jndiProperties;// 防御性地引用 JNDI API用于 JDK 9可选的 java.naming 模块private static final boolean jndiPresent ClassUtils.isPresent(javax.naming.InitialContext, StandardServletEnvironment.class.getClassLoader());public StandardServletEnvironment() {}protected StandardServletEnvironment(MutablePropertySources propertySources) {super(propertySources);}// 自定义属性源集合添加超类贡献的属性源Overrideprotected void customizePropertySources(MutablePropertySources propertySources) {// 添加 ServletConfig 初始化参数占位符属性源propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));// 添加 ServletContext 初始化参数占位符属性源propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));// 如果 JNDI 可用且默认环境可用则添加 JNDI 属性源if (jndiPresent JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));}// 调用父类的自定义属性源方法super.customizePropertySources(propertySources);}// 初始化属性源将 Servlet 相关的属性源如 ServletContext 和 ServletConfig初始化为真实属性源Overridepublic void initPropertySources(Nullable ServletContext servletContext, Nullable ServletConfig servletConfig) {WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);}
}9、ApplicationServletEnvironment AbstractEnvironment构造方法中会调用createPropertyResolver方法也就是创建ApplicationServletEnvironment实例就会创建此自定义属性解析器重写修改自定义属性解析器为ConfigurationPropertySourcesPropertyResolver
class ApplicationServletEnvironment extends StandardServletEnvironment {// 表示不从任何特定的属性如 spring.profiles.active获取活动配置文件Overrideprotected String doGetActiveProfilesProperty() {return null;}// 表示不从任何特定的属性如 spring.profiles.default获取默认配置文件Overrideprotected String doGetDefaultProfilesProperty() {return null;}// 自定义属性解析器// 默认使用ConfigurationPropertySources的实现这里重写实现为ConfigurationPropertySourcesPropertyResolverOverrideprotected ConfigurablePropertyResolver createPropertyResolver(MutablePropertySources propertySources) {return ConfigurationPropertySources.createPropertyResolver(propertySources);}
}三、配置环境
1、命令行参数属性源
// 4.2.配置环境
// 将命令行参数传递给环境配置
configureEnvironment(environment, applicationArguments.getSourceArgs());// 配置环境方法
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {// 如果需要添加转换服务用于在应用程序中管理和执行各种类型之间的转换if (this.addConversionService) {// 设置转换服务为一个新的 ApplicationConversionService 实例environment.setConversionService(new ApplicationConversionService());}// 配置属性源将命令行参数添加到属性源集合中放第一位优先级最高configurePropertySources(environment, args);// 配置活动的配置文件空实现configureProfiles(environment, args);
}将命令行参数属性源添加到环境的属性源集合中且放第一位
/*** 配置属性源的方法。* param environment 可配置的环境对象用于管理属性源* param args 应用程序启动时传入的命令行参数*/
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {// 获取当前环境中的属性源集合MutablePropertySources sources environment.getPropertySources();// 如果存在默认属性defaultProperties将其添加或合并到属性源中if (!CollectionUtils.isEmpty(this.defaultProperties)) {DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources);}// 如果启用了命令行属性解析并且命令行参数非空if (this.addCommandLineProperties args.length 0) {// 获取命令行属性源的默认名称 “commandLineArgs”String name CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;// 如果当前属性源集合中已经包含命令行属性源这里第一次进来不包含if (sources.contains(name)) {// 获取已存在的命令行属性源PropertySource? source sources.get(name);// 创建一个组合属性源用于合并新的和已有的命令行属性CompositePropertySource composite new CompositePropertySource(name);// 将新的命令行参数解析为属性源并添加到组合属性源中composite.addPropertySource(new SimpleCommandLinePropertySource(springApplicationCommandLineArgs, args));// 添加已有的命令行属性源到组合属性源中composite.addPropertySource(source);// 替换旧的命令行属性源为新的组合属性源sources.replace(name, composite);} else {// 如果属性源集合中不存在命令行属性源则直接将解析的命令行属性源添加到最前面sources.addFirst(new SimpleCommandLinePropertySource(args));}}
}2、配置属性源 ConfigurationPropertySourcesPropertySource这个类的作用是将 Spring 配置属性源如 .properties 文件、.yml 文件、环境变量等转换为一个统一的PropertySource并将这些属性源集成到Spring的Environment中。具体解析内容后面单独讲 将名称为configurationProperties对象为SpringConfigurationPropertySources的属性源添加到环境的属性源集合中且放第一位优先级最高。
// 4.2.配置环境
// 添加配置属性源到环境中使其能够解析相关配置属性
ConfigurationPropertySources.attach(environment);public static void attach(Environment environment) {// 确保传入的 environment 是 ConfigurableEnvironment 类型Assert.isInstanceOf(ConfigurableEnvironment.class, environment);// 获取当前环境的属性源集合MutablePropertySources sources ((ConfigurableEnvironment) environment).getPropertySources();// 检查是否已经附加了名为“configurationProperties”的属性源PropertySource? attached getAttached(sources);// 如果尚未附加或者附加的属性源未正确使用当前属性源集合if (attached null || !isUsingSources(attached, sources)) {// 创建一个新的 ConfigurationPropertySourcesPropertySource// 它包装了当前的属性源集合attached new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME, new SpringConfigurationPropertySources(sources));}// 移除已有的同名属性源如果存在sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);// 将新的属性源添加到属性源集合的最前面确保其具有最高优先级sources.addFirst(attached);
}四、触发的应用监听器
// 4.3.通知监听器环境已准备好
listeners.environmentPrepared(bootstrapContext, environment);在之前文章SpringBoot源码解析(三)启动开始阶段已经介绍过广播器将特定的事件之前的应用启动事件现在这里就是准备环境事件推给合适的监听器匹配监听器的事件类型这里就是匹配准备环境事件的监听器。下面我们自己所有匹配到监听器的具体内容。 1、EnvironmentPostProcessorApplicationListener EnvironmentPostProcessorApplicationListener的作用是监听ApplicationEnvironmentPreparedEvent事件加载并执行所有EnvironmentPostProcessor实现类用于在SpringBoot应用启动过程中对环境配置 (Environment) 进行动态调整和扩展例如加载额外的配置源、设置属性或修改激活的 profiles确保在应用上下文初始化之前完成环境的定制化操作。 环境后置处理器EnvironmentPostProcessor的实现类也是从META-INF/spring.factories文件中加载
// 当应用环境准备事件触发时执行的方法
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {// 获取应用的环境配置ConfigurableEnvironment environment event.getEnvironment();// 获取 Spring 应用对象SpringApplication application event.getSpringApplication();// 遍历所有的 EnvironmentPostProcessor环境后置处理器for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),event.getBootstrapContext())) {// 调用每个后置处理器的 postProcessEnvironment 方法处理环境配置postProcessor.postProcessEnvironment(environment, application);}
}// 环境后置处理器接口
FunctionalInterface
public interface EnvironmentPostProcessor {void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application);
}下面看下加载到的所有环境后置处理器 1.1、RandomValuePropertySourceEnvironmentPostProcessor RandomValuePropertySourceEnvironmentPostProcessor是SpringBoot提供的一个内置类用于在Spring应用程序启动时向环境中添加一个RandomValuePropertySource。 它通过 RandomValuePropertySource 提供生成随机值的功能如随机字符串、整数或 UUID供配置文件中使用。
生成随机值 支持 random.int、random.long、random.uuid、random.value 等允许在配置文件中动态生成随机值常用于密钥、端口号等需要唯一性或随机性的场景 在配置文件中用法# 动态生成一个 UUID
my.secret.key${random.uuid}
# 一个介于 1024 和 65535 之间的随机整数
my.random.port${random.int[1024,65535]}RandomValuePropertySource原理很简单就是讲属性源对象设置为new Random() 1.2、SystemEnvironmentPropertySourceEnvironmentPostProcessor 在上面环境实例ApplicationServletEnvironment实例化阶段就添加了环境变量属性源SystemEnvironmentPropertySource这里把它替换成OriginAwareSystemEnvironmentPropertySource主要目的是增强对配置属性来源的追踪能力从而提升可维护性和调试性。
举例
logging.level.org.springframework.core.envDEBUG2024-11-24 12:34:56.123 DEBUG o.s.b.c.c.ConfigFileApplicationListener - Loaded property source applicationConfig: [classpath:/application.properties]
2024-11-24 12:34:56.124 DEBUG o.s.b.c.c.ConfigFileApplicationListener - Loaded property source applicationConfig: [classpath:/application.yml]
2024-11-24 12:34:56.125 DEBUG o.s.b.e.e.PropertySourcesPropertyResolver - Found key my.app.port in systemEnvironment with value 80801.3、SpringApplicationJsonEnvironmentPostProcessor 作用是将环境变量SPRING_APPLICATION_JSON的JSON格式内容解析为配置属性。
举例
export SPRING_APPLICATION_JSON{server.port:8081,spring.datasource.url:jdbc:mysql://localhost:3306/mydb}
java -jar my-app.jar优先级通常高于文件配置如 application.properties 或 application.yml在容器化部署如 Kubernetes、Docker中环境变量是常见的配置传递方式
关键代码片段
Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {String json environment.getProperty(SPRING_APPLICATION_JSON);if (StringUtils.hasText(json)) {try {MapString, Object map parseJson(json);PropertySource? propertySource new MapPropertySource(spring.application.json, map);environment.getPropertySources().addFirst(propertySource);} catch (Exception ex) {throw new IllegalStateException(Invalid SPRING_APPLICATION_JSON: json, ex);}}
}1.4、ConfigDataEnvironmentPostProcessor ConfigDataEnvironmentPostProcessor是SpringBoot2.4引入的一部分作为Spring配置加载机制的新实现的核心组件。它取代了早期的ConfigFileApplicationListener专注于从多种来源如 application.properties、application.yml、环境变量等加载配置数据。具体解析内容后面单独讲
2、AnsiOutputApplicationListener AnsiOutputApplicationListener是SpringBoot提供的一个监听器用于在应用启动时配置ANSI控制台输出彩色日志或彩色信息 的行为。它可以根据环境配置决定是否启用ANSI颜色支持并设置相关属性。
3、BackgroundPreinitializer BackgroundPreinitializer是SpringBoot内置的一个类用于在后台线程中异步加载某些耗时的初始化操作从而减少应用主线程的阻塞时间提高应用启动性能。 主线程可以专注于初始化Spring上下文而耗时的操作如 JUL日志桥接、默认的Validator实例化等在后台进行从而加快应用的总体启动速度。 4、FileEncodingApplicationListener FileEncodingApplicationListener是SpringBoot的一个监听器用于检测和验证 JVM 的文件编码file.encoding属性确保其值符合应用程序的要求。如果文件编码未设置为期望的值可能会引发警告或异常。
五、默认属性源 将DefaultProperties默认属性源移动到Spring环境Environment中属性源的最后面。这通常用于确保用户配置的属性如文件配置、环境变量、命令行参数等优先于默认属性从而允许用户覆盖默认配置。
// 4.4.将默认属性源移到环境属性源列表的末尾
DefaultPropertiesPropertySource.moveToEnd(environment);SpringBoot默认情况下没有添加默认属性源用户可以自定义设置默认值
Configuration
public class DefaultPropertyConfig {Beanpublic PropertySource? defaultPropertySource() {return new MapPropertySource(defaultProperties, Map.of(server.port, 8080));}
}六、绑定spring.main配置到SpringApplication对象
// 4.5.绑定spring.main环境配置到SpringApplication对象上
bindToSpringApplication(environment);它的作用是从ConfigurableEnvironment中提取所有与spring.main前缀相关的配置并将这些配置值赋值给SpringApplication类中的相应字段。 绑定的属性
属性名默认值描述spring.main.banner-modeconsole启动横幅的显示模式默认输出到控制台spring.main.lazy-initializationfalse是否启用懒加载模式spring.main.log-startup-infotrue是否输出启动日志信息spring.main.allow-bean-definition-overriding环境决定true 或 false是否允许覆盖 Bean 定义spring.main.web-application-type自动检测应用类型none、servlet 或 reactivespring.main.register-shutdown-hooktrue是否注册 JVM 的关闭钩子用于资源清理
总结 本文深入探讨了SpringBoot启动过程中应用环境的准备阶段包括从配置文件、命令行参数、系统属性等多种配置源加载属性以及对环境对象进行调整和绑定。通过SpringBoot的灵活机制开发者可以轻松扩展和调整应用环境从而满足各种复杂的场景需求。