首先在 spring-cloud-context
包下找到 spring.factories
文件,其中有这样的配置:
org.springframework.context.ApplicationListener=/ org.springframework.cloud.bootstrap.BootstrapApplicationListener 复制代码
程序启动时,会创建 SpringApplication 对象,此时会遍历 spring.factories
文件,将其中的 ApplicationListener
赋值给 listeners
属性。
public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered { public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap"; public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5; public static final String DEFAULT_PROPERTIES = "defaultProperties"; private int order = DEFAULT_ORDER; @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) { // 1.1 return; } // don't listen to events in a bootstrap context if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { return; } ConfigurableApplicationContext context = null; String configName = environment .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}"); for (ApplicationContextInitializer<?> initializer : event.getSpringApplication() .getInitializers()) { if (initializer instanceof ParentContextApplicationContextInitializer) { context = findBootstrapContext( (ParentContextApplicationContextInitializer) initializer, configName); } } if (context == null) { context = bootstrapServiceContext(environment, event.getSpringApplication(), configName); // 1.2 event.getSpringApplication().addListeners(new CloseContextOnFailureApplicationListener(context)); } apply(context, event.getSpringApplication(), environment); // 1.3 } } 复制代码
BootstrapApplicationListener
监听 ApplicationEnvironmentPreparedEvent
事件;当执行 SpringApplication->prepareEnvironment
方法时,发布事件,执行 onApplicationEvent
方法。
1.1、 spring.cloud.bootstrap.enabled
默认开启,如果设置为 false 的话,直接返回;
1.2、这里面加载了 spring.factories
文件中的 BootstrapConfiguration
;
private ConfigurableApplicationContext bootstrapServiceContext( ConfigurableEnvironment environment, final SpringApplication application, String configName) { ... SpringApplicationBuilder builder = new SpringApplicationBuilder() .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF) .environment(bootstrapEnvironment) // Don't use the default properties in this builder .registerShutdownHook(false).logStartupInfo(false) .web(WebApplicationType.NONE); final SpringApplication builderApplication = builder.application(); ... builder.sources(BootstrapImportSelectorConfiguration.class); // 加载BootstrapConfiguration final ConfigurableApplicationContext context = builder.run(); ... return context; } 复制代码
1.3、把 PropertySourceBootstrapConfiguration
加入到主 SpringApplication 的 initializers 属性中;当调用 SpringApplication -> prepareContext
时,会执行其 initialize
方法。
private void apply(ConfigurableApplicationContext context, SpringApplication application, ConfigurableEnvironment environment) { @SuppressWarnings("rawtypes") List<ApplicationContextInitializer> initializers = getOrderedBeansOfType(context, ApplicationContextInitializer.class); application.addInitializers(initializers .toArray(new ApplicationContextInitializer[initializers.size()])); addBootstrapDecryptInitializer(application); } 复制代码
@Configuration @EnableConfigurationProperties(PropertySourceBootstrapProperties.class) public class PropertySourceBootstrapConfiguration implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered { public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME + "Properties"; private int order = Ordered.HIGHEST_PRECEDENCE + 10; @Autowired(required = false) private List<PropertySourceLocator> propertySourceLocators = new ArrayList<>(); @Override public int getOrder() { return this.order; } public void setPropertySourceLocators( Collection<PropertySourceLocator> propertySourceLocators) { this.propertySourceLocators = new ArrayList<>(propertySourceLocators); } @Override public void initialize(ConfigurableApplicationContext applicationContext) { CompositePropertySource composite = new CompositePropertySource( BOOTSTRAP_PROPERTY_SOURCE_NAME); // 2.1 AnnotationAwareOrderComparator.sort(this.propertySourceLocators); boolean empty = true; ConfigurableEnvironment environment = applicationContext.getEnvironment(); for (PropertySourceLocator locator : this.propertySourceLocators) { PropertySource<?> source = null; source = locator.locate(environment); // 2.2 if (source == null) { continue; } logger.info("Located property source: " + source); composite.addPropertySource(source); // 2.3 empty = false; } if (!empty) { MutablePropertySources propertySources = environment.getPropertySources(); String logConfig = environment.resolvePlaceholders("${logging.config:}"); LogFile logFile = LogFile.get(environment); if (propertySources.contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) { propertySources.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME); } insertPropertySources(propertySources, composite); // 2.4 reinitializeLoggingSystem(environment, logConfig, logFile); setLogLevels(applicationContext, environment); handleIncludedProfiles(environment); } } } 复制代码
2.1、新建 name 为 bootstrapProperties 的 PropertySource
;
2.2、解析远程配置文件的内容;
public org.springframework.core.env.PropertySource<?> locate( org.springframework.core.env.Environment environment) { ConfigClientProperties properties = this.defaultProperties.override(environment); //获取 ConfigClientProperties CompositePropertySource composite = new CompositePropertySource("configService"); RestTemplate restTemplate = this.restTemplate == null ? getSecureRestTemplate(properties) : this.restTemplate; Exception error = null; String errorBody = null; try { String[] labels = new String[] { "" }; if (StringUtils.hasText(properties.getLabel())) { labels = StringUtils .commaDelimitedListToStringArray(properties.getLabel()); } String state = ConfigClientStateHolder.getState(); // Try all the labels until one works for (String label : labels) { Environment result = getRemoteEnvironment(restTemplate, properties, label.trim(), state); // 获取config-server中的配置文件信息,主要是根据 uri,微服务名,profile 环境,label 拼接 url,之后发送 http 请求获取信息 if (result != null) { log(result); if (result.getPropertySources() != null) { for (PropertySource source : result.getPropertySources()) { @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>) source .getSource(); composite.addPropertySource( new MapPropertySource(source.getName(), map)); // 信息保存在 CompositePropertySource 中 } } ... return composite; } } } ... return null; } private Environment getRemoteEnvironment(RestTemplate restTemplate, ConfigClientProperties properties, String label, String state) { String path = "/{name}/{profile}"; String name = properties.getName(); String profile = properties.getProfile(); ... { final HttpEntity<Void> entity = new HttpEntity<>((Void) null, headers); response = restTemplate.exchange(uri + path, HttpMethod.GET, entity, Environment.class, args); //拼接 url,发送 http 请求,获取 config-server 中的文件信息; 比如 uri=http://um31/config/, path= information-service/dev, 那么最终获取的就是 information-service 服务的 dev 环境的配置文件 ... Environment result = response.getBody(); return result; } return null; } 复制代码
2.3、将解析后的结果保存到 composite 中;
2.4、将新的配置加入到 env 环境;
private void insertPropertySources(MutablePropertySources propertySources, CompositePropertySource composite) { MutablePropertySources incoming = new MutablePropertySources(); incoming.addFirst(composite); PropertySourceBootstrapProperties remoteProperties = new PropertySourceBootstrapProperties(); Binder.get(environment(incoming)).bind("spring.cloud.config", Bindable.ofInstance(remoteProperties)); // 根据 remoteProperties 的值来确定配置文件的位置 if (!remoteProperties.isAllowOverride() || (!remoteProperties.isOverrideNone() && remoteProperties.isOverrideSystemProperties())) { propertySources.addFirst(composite); return; } if (remoteProperties.isOverrideNone()) { propertySources.addLast(composite); return; } if (propertySources .contains(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME)) { if (!remoteProperties.isOverrideSystemProperties()) { propertySources.addAfter( StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, composite); } else { propertySources.addBefore( StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, composite); } } else { propertySources.addLast(composite); } } 复制代码
根据 PropertySourceBootstrapProperties
来确定配置文件在 enviroment 中的相对位置。
@ConfigurationProperties("spring.cloud.config") public class PropertySourceBootstrapProperties { /** * Flag to indicate that the external properties should override system properties. * Default true. */ private boolean overrideSystemProperties = true; /** * Flag to indicate that {@link #isOverrideSystemProperties() * systemPropertiesOverride} can be used. Set to false to prevent users from changing * the default accidentally. Default true. */ private boolean allowOverride = true; /** * Flag to indicate that when {@link #setAllowOverride(boolean) allowOverride} is * true, external properties should take lowest priority and should not override any * existing property sources (including local config files). Default false. */ private boolean overrideNone = false; } 复制代码
比如,设置 spring.cloud.config.overrideSystemProperties=false
,那么 env 中 PropertySource 的次序是怎样的呢?
做个实验,
spring.redis.password=gl001
; System.setProperty("spring.redis.password", "gl002");
spring.cloud.config
远程配置文件中设置 spring.redis.password=gl003
; --spring.redis.password=gl004
; public static void main(String[] args) { System.setProperty("logging.level.org.springframework.core.env", "debug"); System.setProperty("spring.redis.password", "gl002"); ConfigurableApplicationContext context = SpringApplication.run(InfoServiceApplication.class, args); System.out.println("spring.redis.password: " + context.getEnvironment().getProperty("spring.redis.password")); Iterator<PropertySource<?>> iter = context.getEnvironment().getPropertySources().iterator(); int i = 0; while (iter.hasNext()) { System.out.println((i++) + ":"); PropertySource source = iter.next(); if (source instanceof CompositePropertySource) { CompositePropertySource compositeSource = (CompositePropertySource) source; System.out.println("name: " + compositeSource.getName() + ", value: " + compositeSource.getPropertySources()); } else { System.out.println("name: " + source.getName() + ", value: " + source.getSource()); } } } 复制代码
部分运行结果如下:
spring.redis.password: gl002 4: name: systemProperties, value: {spring.redis.password=gl002, java.vm.name=Java HotSpot(TM) 64-Bit Server VM, ...} 5: name: systemEnvironment, value: {spring.redis.password=gl004, SHELL=/bin/zsh, ...} 6: name: bootstrapProperties, value: [CompositePropertySource {name='configService', propertySources=[MapPropertySource@1306535359 {name='file:/home/config-center/a-service/a-service.yml', properties={spring.redis.password=gl003, ...}]}] 9: name: applicationConfig: [classpath:/application.yml], value: {spring.redis.password=gl001, server.port=10000,...} 复制代码