一、bootstrap.properties其实是属于spring-cloud的一个环境配置,示例如下

需要添加MAVEN包,否则不会加载bootStrap.properties。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-context</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>

二、bootstrap.properties文件加载是由org.springframework.cloud.bootstrap.

BootstrapApplicationListener在收到ApplicationEnvironmentPreparedEvent event进行处理的。

	public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
		ConfigurableEnvironment environment = event.getEnvironment();
		if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,
				true)) {
			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}");
		if (context == null) {
			context = bootstrapServiceContext(environment, event.getSpringApplication(),
					configName);
		}
		apply(context, event.getSpringApplication(), environment);
	}

 三、在bootstrapServiceContext函数中会设置环境变量属性spring.config.name为bootStrap,然后会创建一个web-application-type为NULL的注解application,这个容器为springcloud生成的容器,到时会作为springboot的父容器,BEAN对象可以共享。application创建成功后,会合并属性对象。

private ConfigurableApplicationContext bootstrapServiceContext(
			ConfigurableEnvironment environment, final SpringApplication application,
			String configName) {
		StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
		MutablePropertySources bootstrapProperties = bootstrapEnvironment
				.getPropertySources();
		for (PropertySource<?> source : bootstrapProperties) {
			bootstrapProperties.remove(source.getName());
		}
		String configLocation = environment
				.resolvePlaceholders("${spring.cloud.bootstrap.location:}");
		Map<String, Object> bootstrapMap = new HashMap<>();
		bootstrapMap.put("spring.config.name", configName);
		// if an app (or test) uses spring.main.web-application-type=reactive, bootstrap will fail
		// force the environment to use none, because if though it is set below in the builder
		// the environment overrides it
		bootstrapMap.put("spring.main.web-application-type", "none");
		if (StringUtils.hasText(configLocation)) {
			bootstrapMap.put("spring.config.location", configLocation);
		}
		bootstrapProperties.addFirst(
				new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));
		for (PropertySource<?> source : environment.getPropertySources()) {
			if (source instanceof StubPropertySource) {
				continue;
			}
			bootstrapProperties.addLast(source);
		}
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		List<String> names = new ArrayList<>(SpringFactoriesLoader
				.loadFactoryNames(BootstrapConfiguration.class, classLoader));
		for (String name : StringUtilsmaDelimitedListToStringArray(
				environment.getProperty("spring.cloud.bootstrap.sources", ""))) {
			names.add(name);
		}
		// TODO: is it possible or sensible to share a ResourceLoader?
		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);
		if (environment.getPropertySources().contains("refreshArgs")) {
			// If we are doing a context refresh, really we only want to refresh the
			// Environment, and there are some toxic listeners (like the
			// LoggingApplicationListener) that affect global static state, so we need a
			// way to switch those off.
			builder.application()
					.setListeners(filterListeners(builder.application().getListeners()));
		}
		List<Class<?>> sources = new ArrayList<>();
		for (String name : names) {
			Class<?> cls = ClassUtils.resolveClassName(name, null);
			try {
				cls.getDeclaredAnnotations();
			}
			catch (Exception e) {
				continue;
			}
			sources.add(cls);
		}
		AnnotationAwareOrderComparator.sort(sources);
		builder.sources(sources.toArray(new Class[sources.size()]));
		final ConfigurableApplicationContext context = builder.run();
		// gh-214 using spring.application.name=bootstrap to set the context id via
		// `ContextIdApplicationContextInitializer` prevents apps from getting the actual
		// spring.application.name
		// during the bootstrap phase.
		context.setId("bootstrap");
		// Make the bootstrap context a parent of the app context
		addAncestorInitializer(application, context);
		// It only has properties in it now that we don't want in the parent so remove
		// it (and it will be added back later)
		bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
		mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
		return context;
	}

 四、由于第三步创建了springcloud的SpringApplication,然后又调用了builder.run,所以会在新的APPLICATION中再次启动RUN流程,会再次prepareEnviment进行预处理环境,所以会再次触发

ApplicationEnvironmentPreparedEvent,接下来会通知到org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEvent,由于上面第三步已经把spring.config.name修改为bootStrap,所以会先读取bootStrap.yaml的配置,跟之前搜索加载application.properties原理一致。

五、在springcloud的应用启动完毕后,我们可以看到环境对象的资源属性列表会添加一个属性。ExtendedDefaultPropertySource {name='defaultProperties'}为BootstrapApplicationListener中的内部属性类。

 六、接下来继续springboot的ApplicationEnvironmentPreparedEvent广播到下一个listener,接下来会由springboot的这个事件通知到org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEvent,这时会去搜索加载application.properties,application-dev.properties,这个前面讲过了,不再复述。

七、当所有的配置文件加载完毕时,我们看下环境对象中的所有属性资源列表

可以看到bootStrap的资源放在最后。从这里可以看到配置文件的优先级。application-dev>application>bootStrap. 

更多推荐

springboot 读取bootStrap.properties流程