說明
在Spring Boot-源碼閱讀-啟動主流程(一) 8-11處觸發了環境變量的加載
<1>
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監聽處理 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(), }
