springboot-01 springboot 啟動 enviroment環境載入
阿新 • • 發佈:2020-07-20
springboot 建立環境
SpringApplication 準備環境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); //預設應用是SERVLET,new StandardServletEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs());// enviroment和args進行繫結,如果配置檔案不是defaultProperty,這個步驟實際上是個空操作 ConfigurationPropertySources.attach(environment);//給enviroment的source中增加key為configurationProperties的SpringConfigurationPropertySources物件 listeners.environmentPrepared(environment);//主要步驟,查詢active的profile bindToSpringApplication(environment);//與啟動類進行繫結 if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; } void environmentPrepared(ConfigurableEnvironment environment) { for (SpringApplicationRunListener listener : this.listeners) { listener.environmentPrepared(environment); } }
EventPublishingRunListener 執行監聽事件
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}catch (ClassCastException ex) {
}
}
ConfigFileApplicationListener 類進行環境載入操作
1.解析環境
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { List<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); postProcessors.add(this);//增加自身例項 AnnotationAwareOrderComparator.sort(postProcessors); for (EnvironmentPostProcessor postProcessor : postProcessors) { postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } }
2.loadPostProcessors()方法獲取spring.factories中EnvironmentPostProcessor 類配置
# Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\ org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\ org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
3.ConfigFileApplicationListener類的postProcessEnvironment方法
載入配置檔案,以yml檔案進行分析;
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());//獲取例項類,如下
}
spring.factories中PropertySourceLoader.class 配置的類
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
4.載入配置的yml或propertis檔案的詳細過程
萬惡的load方法
以YML檔案解析為例,總共涉及到六個load方法,每個方法都處理不同的業務
ConfigFileApplicationListener類的總的載入方法**
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}
根據路徑載入配置檔案,load()方法
-
getSearchLocations()方法獲取配置檔案掃描路徑。預設是DEFAULT_SEARCH_LOCATIONS。
-
配置檔案的名稱names 預設是 DEFAULT_NAMES 也即是application。
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
private static final String DEFAULT_NAMES = "application";
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));//遍歷路徑查詢配置檔案
});
}
根據路徑,查詢yml和properites的配置檔案
根據路徑 + 檔名稱 + 字尾進行配置檔案查詢
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
Set<String> processed = new HashSet<>();
for (PropertySourceLoader loader : this.propertySourceLoaders) {
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
consumer);
}
}
}
}
//PropertiesPropertySourceLoader properties載入器的字尾
public String[] getFileExtensions() {
return new String[] { "properties", "xml" };
}
//YamlPropertySourceLoader yml載入器的字尾
@Override
public String[] getFileExtensions() {
return new String[] { "yml", "yaml" };
}
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
//省略部分程式碼。。。。。。。。。。。。。。。。。
load(loader, prefix + fileExtension, profile, profileFilter, consumer); //這裡的load方法是載入資源的方法,具體過程如下
}
載入資源,配置資料解析
判斷是否為啟用的YML配置檔案,並且獲取YML檔案的配置資訊
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
DocumentConsumer consumer) {
try {
Resource resource = this.resourceLoader.getResource(location);
//。。。。。。。。。。。。。。。。。
String name = "applicationConfig: [" + location + "]";
List<Document> documents = loadDocuments(loader, name, resource); //這裡是yml檔案的載入解析過程,具體如下程式碼
//。。。。。。。。。。。。。。。。
List<Document> loaded = new ArrayList<>();
for (Document document : documents) {
if (filter.match(document)) {
addActiveProfiles(document.getActiveProfiles());//設定啟用yml配置檔案
addIncludedProfiles(document.getIncludeProfiles());
loaded.add(document);
}
}
Collections.reverse(loaded);
if (!loaded.isEmpty()) {
loaded.forEach((document) -> consumer.accept(profile, document));
//。。。。。。。。。。。。。。。。。
}
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load property source from location '" + location + "'", ex);
}
}
//獲取配置檔案解析資料
private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource)
throws IOException {
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
if (documents == null) {
List<PropertySource<?>> loaded = loader.load(name, resource);//又是load方法,這裡是yml的資料解析過程
documents = asDocuments(loaded);
this.loadDocumentsCache.put(cacheKey, documents);
}
return documents;
}
YamlPropertySourceLoader 載入yml配置檔案
這個類是YML檔案的載入解析類,下面的類是具體的解析操作
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
throw new IllegalStateException(
"Attempted to load " + name + " but snakeyaml was not found on the classpath");
}
List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();//解析yml配置資料
if (loaded.isEmpty()) {
return Collections.emptyList();
}
List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
for (int i = 0; i < loaded.size(); i++) {
String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : "";
propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
Collections.unmodifiableMap(loaded.get(i)), true));
}
return propertySources;
}
OriginTrackedYamlLoader類
load()方法用於解析資料,將YML配置引數解析成為MAP集合
List<Map<String, Object>> load() {
final List<Map<String, Object>> result = new ArrayList<>();
process((properties, map) -> result.add(getFlattenedMap(map)));
return result;
}
// YamlProcess類的process方法進行yml檔案解析
protected void process(MatchCallback callback) {
Yaml yaml = createYaml();
for (Resource resource : this.resources) {
boolean found = process(callback, yaml, resource);
if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND && found) {
return;
}
}
}
以上就是springboot啟動時environment的載入過程。