spring: application: name: foo cloud: config: uri: ${SPRING_CONFIG_URI:http://localhost:8888}
tips:我但願經過這篇文章來給對於bootstrap還不理解的朋友帶來幫助。固然這篇文章不只僅是講解知識,我更但願給廣大朋友帶來學習與理解官方文檔的一種思路。閱讀本文前,建議你們對SpringBoot的啓動機制與Environment的做用有大體的瞭解。關於SpringBoot的啓動機制咱們能夠參考:SpringBoot學習之啓動探究 html
SpringCloud爲咱們提供了bootstrap.properties的屬性文件,咱們能夠在該屬性文件裏作咱們的服務配置。但是,咱們知道SpringBoot已經爲咱們提供了作服務配置的屬性文件application.properties,那麼這兩個配置文件有什麼區別呢?在SpringCloud裏是否能用bootstrap代替application作服務的配置?要解決這個問題,咱們必須先討論一下SpringCloud的引導。java
ConfigurableApplicationContext是ApplicationContext的子接口,這裏面有一個方法叫setParent(), 該方法就的做用是設置它的父級ApplicationContext ,注意一旦設置了它的父上下文,後面就不能再次調用setParent方法了。究竟調用這個方法會產生什麼效果呢?下面咱們來看一下源代碼:ios
AbstractApplicationContext的setParent:web
/** * Set the parent of this application context. * <p>The parent {@linkplain ApplicationContext#getEnvironment() environment} is * {@linkplain ConfigurableEnvironment#merge(ConfigurableEnvironment) merged} with * this (child) application context environment if the parent is non-{@code null} and * its environment is an instance of {@link ConfigurableEnvironment}. * @see ConfigurableEnvironment#merge(ConfigurableEnvironment) */ @Override public void setParent(ApplicationContext parent) { this.parent = parent; if (parent != null) { Environment parentEnvironment = parent.getEnvironment(); if (parentEnvironment instanceof ConfigurableEnvironment) { getEnvironment().merge((ConfigurableEnvironment) parentEnvironment); } } }
咱們能夠經過源代碼得知:一旦設置設置父上下文,當前的Environment會合並父上下文的Environment。spring
GenericApplicationContext:express
//....... /** * Create a new GenericApplicationContext with the given parent. * @param parent the parent application context * @see #registerBeanDefinition * @see #refresh */ public GenericApplicationContext(ApplicationContext parent) { this(); setParent(parent); } // ..... /** * Set the parent of this application context, also setting * the parent of the internal BeanFactory accordingly. * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#setParentBeanFactory */ @Override public void setParent(ApplicationContext parent) { super.setParent(parent); this.beanFactory.setParentBeanFactory(getInternalParentBeanFactory()); }
經過源代碼得知:該類不只會合並Environment還會把父上下文的BeanFactory"借用過來" ,咱們經常使用的ClasspathXmlApplicationContext是AbstractApplicationContext的子類,而AnnotationConfigApplicationContext是GenericApplicationContext的子類apache
首先咱們先建一個屬性文件application.properties,在屬性文件裏配置:bootstrap
jdbc.user=root
而後咱們按照以下目錄創建好相關文件:服務器
StudentConfig:app
package org.hzgj.spring.study.student; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration @ComponentScan @PropertySource("application.properties") public class StudentConfig { }
TeacherConfig:
package org.hzgj.spring.study.teacher; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan public class TeacherConfig { }
Student:
package org.hzgj.spring.study.student; import org.hzgj.spring.study.teacher.Teacher; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class Student { @Value("${jdbc.user}") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } private int age=20; public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
Teacher:
package org.hzgj.spring.study.teacher; import org.springframework.stereotype.Component; @Component public class Teacher { private String name = "張老師"; public String getName() { return name; } public void setName(String name) { this.name = name; } }
Main方法:
package org.hzgj.spring.study; import org.hzgj.spring.study.student.StudentConfig; import org.hzgj.spring.study.student.Student; import org.hzgj.spring.study.teacher.TeacherConfig; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import javax.naming.NamingException; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException, NamingException { AnnotationConfigApplicationContext studentApplicationContext = new AnnotationConfigApplicationContext(StudentConfig.class); AnnotationConfigApplicationContext teacherApplicationContext = new AnnotationConfigApplicationContext(TeacherConfig.class); teacherApplicationContext.setParent(studentApplicationContext); Student student = teacherApplicationContext.getBean(Student.class); System.out.println("獲取student對象的name屬性:" + student.getName()); System.out.println(studentApplicationContext.getEnvironment().getProperty("jdbc.user")); } }
在這裏咱們將Teacher的父級上下文設置成student的,運行獲得以下結果:
我在這裏先貼出官方文檔的一段描述:
一個Spring Cloud應用程序經過建立一個「引導」上下文來進行操做,這個上下文是主應用程序的父上下文。開箱即用,負責從外部源加載配置屬性,還解密本地外部配置文件中的屬性。這兩個上下文共享一個Environment
,這是任何Spring應用程序的外部屬性的來源。Bootstrap屬性的優先級高,所以默認狀況下不能被本地配置覆蓋。
引導上下文使用與主應用程序上下文不一樣的外部配置約定,所以使用bootstrap.yml
application.yml
(或.properties
)代替引導和主上下文的外部配置。例:bootstrap.yml
spring: application: name: foo cloud: config: uri: ${SPRING_CONFIG_URI:http://localhost:8888}
若是您的應用程序須要服務器上的特定於應用程序的配置,那麼設置spring.application.name
(在bootstrap.yml
或application.yml
)中是個好主意。
您能夠經過設置spring.cloud.bootstrap.enabled=false
(例如在系統屬性中)來徹底禁用引導過程。
初看這段話的朋友,可能會比較蒙圈,不要緊我來解釋幾個關鍵點:
引導上下文,這個是什麼意思呢?咱們能夠把這個理解爲springcloud的"bios"。咱們能夠先看一下這個引導到底在哪裏:
在這裏咱們能夠發現幾個關鍵的類,其中BootstrapApplicationListener是核心中的核心:我在這裏貼一下源代碼:
/* * Copyright 2013-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.cloud.bootstrap; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.boot.Banner.Mode; import org.springframework.boot.SpringApplication; import org.springframework.boot.builder.ParentContextApplicationContextInitializer; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.logging.LoggingApplicationListener; import org.springframework.cloud.bootstrap.encrypt.EnvironmentDecryptApplicationInitializer; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.Order; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.SystemEnvironmentPropertySource; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** * A listener that prepares a SpringApplication (e.g. populating its Environment) by * delegating to {@link ApplicationContextInitializer} beans in a separate bootstrap * context. The bootstrap context is a SpringApplication created from sources defined in * spring.factories as {@link BootstrapConfiguration}, and initialized with external * config taken from "bootstrap.properties" (or yml), instead of the normal * "application.properties". * * @author Dave Syer * */ 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)) { 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); } apply(context, event.getSpringApplication(), environment); } private ConfigurableApplicationContext findBootstrapContext( ParentContextApplicationContextInitializer initializer, String configName) { Field field = ReflectionUtils .findField(ParentContextApplicationContextInitializer.class, "parent"); ReflectionUtils.makeAccessible(field); ConfigurableApplicationContext parent = safeCast( ConfigurableApplicationContext.class, ReflectionUtils.getField(field, initializer)); if (parent != null && !configName.equals(parent.getId())) { parent = safeCast(ConfigurableApplicationContext.class, parent.getParent()); } return parent; } private <T> T safeCast(Class<T> type, Object object) { try { return type.cast(object); } catch (ClassCastException e) { return null; } } 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 (StringUtils.hasText(configLocation)) { bootstrapMap.put("spring.config.location", configLocation); } bootstrapProperties.addFirst( new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap)); for (PropertySource<?> source : environment.getPropertySources()) { bootstrapProperties.addLast(source); } ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates List<String> names = SpringFactoriesLoader .loadFactoryNames(BootstrapConfiguration.class, classLoader); for (String name : StringUtils.commaDelimitedListToStringArray( 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) .properties("spring.application.name:" + configName) .registerShutdownHook(false).logStartupInfo(false).web(false); 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(); // 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; } private Collection<? extends ApplicationListener<?>> filterListeners( Set<ApplicationListener<?>> listeners) { Set<ApplicationListener<?>> result = new LinkedHashSet<>(); for (ApplicationListener<?> listener : listeners) { if (!(listener instanceof LoggingApplicationListener) && !(listener instanceof LoggingSystemShutdownListener)) { result.add(listener); } } return result; } private void mergeDefaultProperties(MutablePropertySources environment, MutablePropertySources bootstrap) { String name = DEFAULT_PROPERTIES; if (!bootstrap.contains(name)) { return; } PropertySource<?> source = bootstrap.get(name); if (source instanceof MapPropertySource) { Map<String, Object> map = ((MapPropertySource) source).getSource(); // The application name is "bootstrap" (by default) at this point and // we don't want that to appear in the parent context at all. map.remove("spring.application.name"); } if (!environment.contains(name)) { environment.addLast(source); } else { PropertySource<?> target = environment.get(name); if (target instanceof MapPropertySource) { Map<String, Object> targetMap = ((MapPropertySource) target).getSource(); if (target == source) { return; } if (source instanceof MapPropertySource) { Map<String, Object> map = ((MapPropertySource) source).getSource(); for (String key : map.keySet()) { if (!target.containsProperty(key)) { targetMap.put(key, map.get(key)); } } } } } mergeAdditionalPropertySources(environment, bootstrap); } private void mergeAdditionalPropertySources(MutablePropertySources environment, MutablePropertySources bootstrap) { PropertySource<?> defaultProperties = environment.get(DEFAULT_PROPERTIES); ExtendedDefaultPropertySource result = defaultProperties instanceof ExtendedDefaultPropertySource ? (ExtendedDefaultPropertySource) defaultProperties : new ExtendedDefaultPropertySource(defaultProperties.getName(), defaultProperties); for (PropertySource<?> source : bootstrap) { if (!environment.contains(source.getName())) { result.add(source); } } for (String name : result.getPropertySourceNames()) { bootstrap.remove(name); } environment.replace(DEFAULT_PROPERTIES, result); bootstrap.replace(DEFAULT_PROPERTIES, result); } private void addAncestorInitializer(SpringApplication application, ConfigurableApplicationContext context) { boolean installed = false; for (ApplicationContextInitializer<?> initializer : application .getInitializers()) { if (initializer instanceof AncestorInitializer) { installed = true; // New parent ((AncestorInitializer) initializer).setParent(context); } } if (!installed) { application.addInitializers(new AncestorInitializer(context)); } } 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); } private void addBootstrapDecryptInitializer(SpringApplication application) { DelegatingEnvironmentDecryptApplicationInitializer decrypter = null; for (ApplicationContextInitializer<?> initializer : application .getInitializers()) { if (initializer instanceof EnvironmentDecryptApplicationInitializer) { @SuppressWarnings("unchecked") ApplicationContextInitializer<ConfigurableApplicationContext> delegate = (ApplicationContextInitializer<ConfigurableApplicationContext>) initializer; decrypter = new DelegatingEnvironmentDecryptApplicationInitializer( delegate); } } if (decrypter != null) { application.addInitializers(decrypter); } } private <T> List<T> getOrderedBeansOfType(ListableBeanFactory context, Class<T> type) { List<T> result = new ArrayList<T>(); for (String name : context.getBeanNamesForType(type)) { result.add(context.getBean(name, type)); } AnnotationAwareOrderComparator.sort(result); return result; } public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } private static class AncestorInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered { private ConfigurableApplicationContext parent; public AncestorInitializer(ConfigurableApplicationContext parent) { this.parent = parent; } public void setParent(ConfigurableApplicationContext parent) { this.parent = parent; } @Override public int getOrder() { // Need to run not too late (so not unordered), so that, for instance, the // ContextIdApplicationContextInitializer runs later and picks up the merged // Environment. Also needs to be quite early so that other initializers can // pick up the parent (especially the Environment). return Ordered.HIGHEST_PRECEDENCE + 5; } @Override public void initialize(ConfigurableApplicationContext context) { while (context.getParent() != null && context.getParent() != context) { context = (ConfigurableApplicationContext) context.getParent(); } reorderSources(context.getEnvironment()); new ParentContextApplicationContextInitializer(this.parent) .initialize(context); } private void reorderSources(ConfigurableEnvironment environment) { PropertySource<?> removed = environment.getPropertySources() .remove(DEFAULT_PROPERTIES); if (removed instanceof ExtendedDefaultPropertySource) { ExtendedDefaultPropertySource defaultProperties = (ExtendedDefaultPropertySource) removed; environment.getPropertySources().addLast(new MapPropertySource( DEFAULT_PROPERTIES, defaultProperties.getSource())); for (PropertySource<?> source : defaultProperties.getPropertySources() .getPropertySources()) { if (!environment.getPropertySources().contains(source.getName())) { environment.getPropertySources().addBefore(DEFAULT_PROPERTIES, source); } } } } } /** * A special initializer designed to run before the property source bootstrap and * decrypt any properties needed there (e.g. URL of config server). */ @Order(Ordered.HIGHEST_PRECEDENCE + 9) private static class DelegatingEnvironmentDecryptApplicationInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { private ApplicationContextInitializer<ConfigurableApplicationContext> delegate; public DelegatingEnvironmentDecryptApplicationInitializer( ApplicationContextInitializer<ConfigurableApplicationContext> delegate) { this.delegate = delegate; } @Override public void initialize(ConfigurableApplicationContext applicationContext) { this.delegate.initialize(applicationContext); } } private static class ExtendedDefaultPropertySource extends SystemEnvironmentPropertySource { private final CompositePropertySource sources; private final List<String> names = new ArrayList<>(); public ExtendedDefaultPropertySource(String name, PropertySource<?> propertySource) { super(name, findMap(propertySource)); this.sources = new CompositePropertySource(name); } public CompositePropertySource getPropertySources() { return this.sources; } public List<String> getPropertySourceNames() { return this.names; } public void add(PropertySource<?> source) { if (source instanceof EnumerablePropertySource && !this.names.contains(source.getName())) { this.sources.addPropertySource(source); this.names.add(source.getName()); } } @Override public Object getProperty(String name) { if (this.sources.containsProperty(name)) { return this.sources.getProperty(name); } return super.getProperty(name); } @Override public boolean containsProperty(String name) { if (this.sources.containsProperty(name)) { return true; } return super.containsProperty(name); } @Override public String[] getPropertyNames() { List<String> names = new ArrayList<>(); names.addAll(Arrays.asList(this.sources.getPropertyNames())); names.addAll(Arrays.asList(super.getPropertyNames())); return names.toArray(new String[0]); } @SuppressWarnings("unchecked") private static Map<String, Object> findMap(PropertySource<?> propertySource) { if (propertySource instanceof MapPropertySource) { return (Map<String, Object>) propertySource.getSource(); } return new LinkedHashMap<String, Object>(); } } }
這個類是一個監聽器,它用於監聽ApplicationEnvironmentPreparedEvent事件,而EventPublishingRunListener在SpringBoot啓動時會觸發該事件。若是不理解的這個類的朋友請務必先了解SpringBoot啓動過程
這個工做主要分爲兩個層面:1.建立上下文引導 2.設置爲其爲當前程序的父級上下文
1) 咱們先看看onApplicationEvent方法,該方法首先讀取spring.cloud.bootstrap.enabled的屬性值若是爲false,那麼就直接return。這也就是官方文檔裏的說明能夠用此屬性禁用引導的理由。
2)緊接着它會從當前應用程序SpringApplication試着在全部的ApplicationInitializer中獲取ParentContextApplicationContextInitializer,若是找到的話就把該類下的parent作爲引導上下文。
3)若是沒有找到ParentContextApplicationContextInitializer,則經過 bootstrapServiceContext方法來建立引導上下文,其中以下代碼請你們留意下:
List<String> names = SpringFactoriesLoader .loadFactoryNames(BootstrapConfiguration.class, classLoader);
看到SpringFactoriesLoader不用想必定會在META-INF/spring.factoies裏找配置的BootstrapConfiguration的進行實例化
4)經過以下代碼建立引導上下文對象:
SpringApplicationBuilder builder = new SpringApplicationBuilder() .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF) .environment(bootstrapEnvironment) .properties("spring.application.name:" + configName) .registerShutdownHook(false).logStartupInfo(false).web(false); 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();
5)最後經過以下方法設置引導上下文爲當前應用程序的上下文:
// Make the bootstrap context a parent of the app context addAncestorInitializer(application, context);
開箱即用,理解起來很簡單。經過2.2分析,引導程序在SpringBoot的啓動前就幫咱們建立好了,固然也就開箱即用了。
下面咱們看一下spring-cloud-context.jar下的META-INF/spring.factoies文件:
# AutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\ org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\ org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\ org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.cloud.bootstrap.BootstrapApplicationListener,\ org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\ org.springframework.cloud.context.restart.RestartListener # Bootstrap components org.springframework.cloud.bootstrap.BootstrapConfiguration=\ org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\ org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\ org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
咱們來看一下 BootstrapConfiguration下面配置的引導程序類:
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration:這個類主要解析加載外部化配置屬性
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration:主要配置文件中前綴爲{cipher}的相關解密,熟悉spring-boot-starter-security在springcloud應用的朋友必定不陌生
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration:主要是監聽EnvironmentChangeEvent事件用於刷新@ConfigurationProperties標記的配置
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration:主要解析配置文件中的${}佔位符
Environment
,這是任何Spring應用程序的外部屬性的來源。既然引導上下文爲當前主程序的父級上下文,那麼就能夠肯定他們共享Environment,至於爲何請閱讀文章第一部分
要解釋這個咱們必須用代碼來演示了,結構圖:
注意:MyBootstrapAutoConfiguration是咱們自定義的引導類,該類必定不能被@SpringBootApplication註解ComponentScan到,不然引導必然就會被主程序所覆蓋。所以我用包把他們區分開來
MyBootstrapAutoConfiguration代碼:
package com.bdqn.lyrk.bootstrap.config; import com.bdqn.lyrk.bootstrap.server.BootStrapConfig; import com.bdqn.lyrk.bootstrap.server.Student; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @EnableConfigurationProperties(BootStrapConfig.class) public class MyBootstrapAutoConfiguration { @Bean public Student student(BootStrapConfig bootStrapConfig){ Student student = new Student(); student.setName(bootStrapConfig.getName()); return student; } }
BootstrapConfig:
package com.bdqn.lyrk.bootstrap.server; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties("student") public class BootStrapConfig { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
Student:
package com.bdqn.lyrk.bootstrap.server; public class Student { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
application.yml:
student:
name: application
bootstrap.yml:
student:
name: bootstrap
spring.factories:
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.bdqn.lyrk.bootstrap.config.MyBootstrapAutoConfiguration
啓動類代碼:
package com.bdqn.lyrk.bootstrap.server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication() public class BootstrapServer { public static void main(String[] args) { ConfigurableApplicationContext applicationContext = SpringApplication.run(BootstrapServer.class, args); Student student = applicationContext.getBean(Student.class); System.out.println(student.getName()); } }
運行後獲得結果:
所以咱們能夠看到對於引導程序bootstrap.yml比application.yml優先級更高,更不可能被application.yml文件裏的所覆蓋
1)引導程序上下文在prepareEnvironment的階段就會被建立,建立時會讀取bootstrap.properties|yml 在內容做爲引導配置, 所以bootstrap優先於application加載。引導程序很是相似於bios,而bootstrap.application就至關於設置bios的相關參數
2)boostrap的屬性文件在如下情景下會使用:
配置中心:config-server裏請用bootstrap屬性文件
解密屬性文件時,最好使用bootstrap屬性文件
須要自定義引導程序時使用bootstrap屬性文件,主要必定不要被咱們主程序掃描到
3)application會覆蓋bootstrap中的非引導配置,所以不建議兩種類型配置文件同時存在。簡單粗暴的作法是在springcloud應用中用bootstrap屬性文件代替application一統江湖嘛,畢竟Envrionment是共享的。
4) 在閱讀官方文檔時,必定要結合源代碼深刻分析,才能更好的理解其用意