spring-boot-源码阅读-如何加载环境变量(二)
说明
在Spring Boot-源码阅读-启动主流程(一) 8-11处触发了环境变量的加载
时序图
<1>
org.springframework.boot.SpringApplication#prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // <2>内部封装的PropertySource集合 封装各个环境变量 如默认会初始化系统环境变量 和jvm环境变量 ConfigurableEnvironment environment = getOrCreateEnvironment(); //将系统变量设置到environment configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); //<4>发送listener ApplicationEnviromentPreparedEvent 对应消费将触发我们application.yml配置文件加载 /** * # Run Listeners * org.springframework.boot.SpringApplicationRunListener=\ * org.springframework.boot.context.event.EventPublishingRunListener 默认 */ listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = convertEnvironment(environment); } ConfigurationPropertySources.attach(environment); return environment; }
<2>
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } switch (this.webApplicationType) { case SERVLET: //<3>默认返回此 父类会触发初始化默认的PropertySource return new ApplicationServletEnvironment(); case REACTIVE: return new ApplicationReactiveWebEnvironment(); default: return new ApplicationEnvironment(); } }
<3>
继承链
org.springframework.core.env.AbstractEnvironment
public AbstractEnvironment() { this(new MutablePropertySources()); } protected AbstractEnvironment(MutablePropertySources propertySources) { this.propertySources = propertySources;
//当我调用get 就是委托给Resolver 遍历调用propertSource获取对应的值。比如我们可以实现自定义key解析 get("${age}")内部委托给propertySroce就传age this.propertyResolver = createPropertyResolver(propertySources); //<3-1>被子类重写 StandardServletEnvironment customizePropertySources(propertySources); }
<3-1>
org.springframework.web.context.support.StandardServletEnvironment#customizePropertySources
public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams"; public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams"; protected void customizePropertySources(MutablePropertySources propertySources) { //新增2个servlet相关的propertySource 但是是空的 propertySources.addLast(new PropertySource.StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME)); propertySources.addLast(new PropertySource.StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME)); if (jndiPresent && JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) { propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME)); } //<3-2>调用父类StandardEnvironment 的自定义方法 super.customizePropertySources(propertySources); }
<3-2>
org.springframework.core.env.StandardEnvironment#customizePropertySources
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment"; public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties"; @Override protected void customizePropertySources(MutablePropertySources propertySources) { //新增2各个System相关环境变量 并加载 //内部调用System.getProperties() 获得map jvm参数相关环境变量 propertySources.addLast( new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties())); //内部调用 (Map) System.getenv() 获得map 主要获取系统相关环境变量 propertySources.addLast( new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); }
<4>
//EventPublishingRunListener构造函数 public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.initialMulticaster = new SimpleApplicationEventMulticaster(); //EventPublishingRunListener初始化的时候会初始化一个广播器,注册了我们ApplicationListener监听器 初始化处可以查看<点击跳转> for (ApplicationListener<?> listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } } public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { //<5>发送ApplicationEnvironmentPreparedEvent事件 将由监听器org.springframework.boot.env.EnvironmentPostProcessorApplicationListener监听处理 在spring.boot由ConfigFileApplicationListener this.initialMulticaster.multicastEvent( new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment)); }
<5>
我们主要关注org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor 这个就是如何加载application.yml
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener#onApplicationEnvironmentPreparedEvent
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); SpringApplication application = event.getSpringApplication(); //<6>这里也是获取spring.factories的EnvironmentPostProcessor实现类 这里也是我们加载自定义环境变量的扩展点 /** * # Environment Post Processors * org.springframework.boot.env.EnvironmentPostProcessor=\ * org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ * org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor,\ * org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor,\ 针对于随机数可以自行百度 如@value(${random.int}),@value(${random.int[1,1024]}), * org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\ 针对json的配置如:-d {"age":12} @value(${age}) * org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\ * org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor */ for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(), event.getBootstrapContext())) { postProcessor.postProcessEnvironment(environment, application); } }
<6>
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor#postProcessEnvironment
@Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { postProcessEnvironment(environment, application.getResourceLoader(), application.getAdditionalProfiles()); } void postProcessEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles) { try { this.logger.trace("Post-processing environment to add config data"); resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(); //委托给ConfigDataEnvironment 触发加载<7>初始化 <10>加载 getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).processAndApply(); } catch (UseLegacyConfigProcessingException ex) { this.logger.debug(LogMessage.format("Switching to legacy config file processing [%s]", ex.getConfigurationProperty())); configureAdditionalProfiles(environment, additionalProfiles); postProcessUsingLegacyApplicationListener(environment, resourceLoader); } }
<7>
org.springframework.boot.context.config.ConfigDataEnvironment#ConfigDataEnvironment
ConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles, ConfigDataEnvironmentUpdateListener environmentUpdateListener) { Binder binder = Binder.get(environment); UseLegacyConfigProcessingException.throwIfRequested(binder); this.logFactory = logFactory; this.logger = logFactory.getLog(getClass()); this.notFoundAction = binder.bind(ON_NOT_FOUND_PROPERTY, ConfigDataNotFoundAction.class) .orElse(ConfigDataNotFoundAction.FAIL); this.bootstrapContext = bootstrapContext; this.environment = environment; this.resolvers = createConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader); this.additionalProfiles = additionalProfiles; this.environmentUpdateListener = (environmentUpdateListener != null) ? environmentUpdateListener : ConfigDataEnvironmentUpdateListener.NONE; this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext, resourceLoader.getClassLoader()); //<8>我的理解这个是解析器 this.contributors = createContributors(binder); }
<8>
org.springframework.boot.context.config.ConfigDataEnvironment#createContributors
private ConfigDataEnvironmentContributors createContributors(Binder binder) { this.logger.trace("Building config data environment contributors"); MutablePropertySources propertySources = this.environment.getPropertySources(); List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>(propertySources.size() + 10); PropertySource<?> defaultPropertySource = null; for (PropertySource<?> propertySource : propertySources) { if (DefaultPropertiesPropertySource.hasMatchingName(propertySource)) { defaultPropertySource = propertySource; } else { this.logger.trace(LogMessage.format("Creating wrapped config data contributor for '%s'", propertySource.getName())); contributors.add(ConfigDataEnvironmentContributor.ofExisting(propertySource)); } } //<9>主要是这里创建负责解析指定目录文件下配置文件的解析器 contributors.addAll(getInitialImportContributors(binder)); if (defaultPropertySource != null) { this.logger.trace("Creating wrapped config data contributor for default property source"); contributors.add(ConfigDataEnvironmentContributor.ofExisting(defaultPropertySource)); } return createContributors(contributors); }
<9>
org.springframework.boot.context.config.ConfigDataEnvironment#getInitialImportContributors
static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS; static { List<ConfigDataLocation> locations = new ArrayList<>(); locations.add(ConfigDataLocation.of("optional:classpath:/;optional:classpath:/config/")); locations.add(ConfigDataLocation.of("optional:file:./;optional:file:./config/;optional:file:./config/*/")); DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]); } private List<ConfigDataEnvironmentContributor> getInitialImportContributors(Binder binder) { List<ConfigDataEnvironmentContributor> initialContributors = new ArrayList<>(); addInitialImportContributors(initialContributors, bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS)); addInitialImportContributors(initialContributors, bindLocations(binder, ADDITIONAL_LOCATION_PROPERTY, EMPTY_LOCATIONS)); //主要是这里新增了从哪些目录下查找我们的properties文件 指定定义还未加载 addInitialImportContributors(initialContributors, bindLocations(binder, LOCATION_PROPERTY, DEFAULT_SEARCH_LOCATIONS)); return initialContributors; }
<10>
org.springframework.boot.context.config.ConfigDataEnvironment#processAndApply
void processAndApply() { ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers, this.loaders); registerBootstrapBinder(this.contributors, null, DENY_INACTIVE_BINDING); //这里先加载我们的application.yml文件 ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer); ConfigDataActivationContext activationContext = createActivationContext( contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE)); //根据application.yml配置的环境,加载对应的yml文件 /** * 如 * spring: * profiles: * active: dev,test */ contributors = processWithoutProfiles(contributors, importer, activationContext); //解析出环境配置如 dev,test activationContext = withProfiles(contributors, activationContext); //进行对应环境的yml文件加载 contributors = processWithProfiles(contributors, importer, activationContext); //转成PropertySource add到Environment applyToEnvironment(contributors, activationContext, importer.getLoadedLocations(), }
applo扩展点:
com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
2019-02-26 elasticsearch-排序(六)