Based on the org. Springframework. Cloud: spring — cloud – config: 2.1.0. RC3 version for analysis.

1 BootstrapApplicationListener

Start by finding the spring.factories file in the spring-Cloud-Context package with the following configuration:

org.springframework.context.ApplicationListener=\
org.springframework.cloud.bootstrap.BootstrapApplicationListener
Copy the code

When the program starts, the SpringApplication object is created and the Spring.Factories file is iterated over, assigning the ApplicationListener in it to the Listeners property.

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 instanceofParentContextApplicationContextInitializer) { 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}}Copy the code

BootstrapApplicationListener listening ApplicationEnvironmentPreparedEvent events; When the SpringApplication->prepareEnvironment method is executed, the event is published and the onApplicationEvent method is executed.

1.1, spring. Cloud. The bootstrap. Enabled by default open, if set to false, direct return;

The BootstrapConfiguration file in the Spring. factories file is loaded.

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);
	finalSpringApplication builderApplication = builder.application(); . builder.sources(BootstrapImportSelectorConfiguration.class);/ / load BootstrapConfiguration
	finalConfigurableApplicationContext context = builder.run(); .return context;
}
Copy the code

1.3, add PropertySourceBootstrapConfiguration to advocate the initializers SpringApplication properties; The Initialize method is executed when SpringApplication -> prepareContext is called.

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);
}
Copy the code

2 PropertySourceBootstrapConfiguration

@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(a) {
		return this.order;
	}

	public void setPropertySourceLocators( Collection
       
         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.4reinitializeLoggingSystem(environment, logConfig, logFile); setLogLevels(applicationContext, environment); handleIncludedProfiles(environment); }}}Copy the code

2.1. Create PropertySource whose name is bootstrapProperties;

2.2. Parse the contents of the remote configuration file.

publicorg.springframework.core.env.PropertySource<? > locate( org.springframework.core.env.Environment environment) { ConfigClientProperties properties =this.defaultProperties.override(environment);  / / get 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);  // To obtain the configuration file information in config-server, the url is mainly assembled according to uri, micro service name, profile environment, label, and then send HTTP request to obtain the information
			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));  // The information is stored in CompositePropertySource}}...returncomposite; }}}...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);  // Join url, send HTTP request, get file information in config-server; For example, if uri=http://um31/config/, path= information-service/dev, the configuration file of the information-service dev environment is obtained. Environment result = response.getBody();return result;
	}
	return null;
}
Copy the code

2.3. Save the parsed result in composite;

2.4. Add the new configuration to 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));
	// Locate the configuration file based on the value of 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); }}Copy the code

3 PropertySourceBootstrapProperties

According to the PropertySourceBootstrapProperties to determine the relative position in the enviroment configuration file.

@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;
}
Copy the code
  • AllowOverride is false and precedes; OverrideNone is false and overrideSystemProperties is true first;
  • OverrideNone is true and comes last;
  • If the systemEnvironment configuration overrideSystemProperties is false, place it after systemEnvironment. Otherwise, place it in front of the systemEnvironment;
  • Everything else is at the bottom.

For example, set up spring. Cloud. Config. OverrideSystemProperties = false, so in the env PropertySource order?

To do an experiment,

  1. Application. Yml Settingsspring.redis.password=gl001;
  2. Configured in the main functionSystem.setProperty("spring.redis.password", "gl002");
  3. spring.cloud.configSet in the remote configuration filespring.redis.password=gl003;
  4. Set when the program starts--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()); }}}Copy the code

Some running results are as follows:

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. }Copy the code