SpringBoot的application.yml配置文件加载流程分析

application.yml 的加载流程分析

application.yml 文件对于 Spring Boot 来说是核心配置文件,至关重要,那么,该文件是如何加载到内存的呢?需要从启动类的 run()方法开始跟踪。

1. 启动方法 run()跟踪

@SpringBootApplication
public class Application {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(Application.class, args);
    }

}
//org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String...)
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    
    
	return run(new Class<?>[] {
    
     primarySource }, args);
}
//org.springframework.boot.SpringApplication#run(java.lang.Class<?>[], java.lang.String[])
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    
    
	return new SpringApplication(primarySources).run(args);
}

直接跟SpringApplication的run方法:

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
 //org.springframework.boot.SpringApplication#run(java.lang.String...)
public ConfigurableApplicationContext run(String... args) {
    
    
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	configureHeadlessProperty();
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
    
    
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		//准备运行环境
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] {
    
     ConfigurableApplicationContext.class }, context);
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
    
    
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
    
    
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
    
    
		listeners.running(context);
	}
	catch (Throwable ex) {
    
    
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

2. 准备运行环境

看prepareEnvironment方法:

//org.springframework.boot.SpringApplication#prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments) {
    
    
	// Create and configure the environment
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	ConfigurationPropertySources.attach(environment);
	//为准备过程添加监听
	listeners.environmentPrepared(environment);
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
    
    
		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
				deduceEnvironmentClass());
	}
	ConfigurationPropertySources.attach(environment);
	return environment;
}

3. 为准备过程添加监听

//org.springframework.boot.SpringApplicationRunListeners#environmentPrepared
public void environmentPrepared(ConfigurableEnvironment environment) {
    
    
	for (SpringApplicationRunListener listener : this.listeners) {
    
    
		listener.environmentPrepared(environment);
	}
}

DEBUG:
在这里插入图片描述

//org.springframework.boot.context.event.EventPublishingRunListener#environmentPrepared
public void environmentPrepared(ConfigurableEnvironment environment) {
    
    
	//广播环境准备事件
	this.initialMulticaster
			.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}

4. 广播环境准备事件

在这里插入图片描述

//org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent)
public void multicastEvent(ApplicationEvent event) {
    
    
	multicastEvent(event, resolveDefaultEventType(event));
}
//org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    
    
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	Executor executor = getTaskExecutor();
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
    
    
		if (executor != null) {
    
    
			//触发监听器
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
    
    
			//触发监听器
			invokeListener(listener, event);
		}
	}
}

5. 触发监听器

//org.springframework.context.event.SimpleApplicationEventMulticaster#invokeListener
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    
    
	ErrorHandler errorHandler = getErrorHandler();
	if (errorHandler != null) {
    
    
		try {
    
    
			doInvokeListener(listener, event);
		}
		catch (Throwable err) {
    
    
			errorHandler.handleError(err);
		}
	}
	else {
    
    
		doInvokeListener(listener, event);
	}
}
//org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    
    
	try {
    
    
		listener.onApplicationEvent(event);
	}
	catch (ClassCastException ex) {
    
    
		String msg = ex.getMessage();
		if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
    
    
			// Possibly a lambda-defined listener which we could not resolve the generic event type for
			// -> let's suppress the exception and just log a debug message.
			Log logger = LogFactory.getLog(getClass());
			if (logger.isTraceEnabled()) {
    
    
				logger.trace("Non-matching event type for listener: " + listener, ex);
			}
		}
		else {
    
    
			throw ex;
		}
	}
}

在这里插入图片描述

//org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEvent
public void onApplicationEvent(ApplicationEvent event) {
    
    
	if (event instanceof ApplicationEnvironmentPreparedEvent) {
    
    
		//环境准备事件
		onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
	}
	if (event instanceof ApplicationPreparedEvent) {
    
    
		onApplicationPreparedEvent(event);
	}
}
//org.springframework.boot.context.config.ConfigFileApplicationListener#onApplicationEnvironmentPreparedEvent
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());
	}
}

DEBUG:
在这里插入图片描述

主要关注配置文件应用监听:
在这里插入图片描述

//org.springframework.boot.context.config.ConfigFileApplicationListener#postProcessEnvironment
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
    
    
	addPropertySources(environment, application.getResourceLoader());
}
//org.springframework.boot.context.config.ConfigFileApplicationListener#addPropertySources
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
    
    
	RandomValuePropertySource.addToEnvironment(environment);
	//load:加载配置文件
	new Loader(environment, resourceLoader).load();
}

6. 加载配置文件

//org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load()
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());
					}
					//根据指定profile加载
					load(profile, this::getPositiveProfileFilter,
							addToLoaded(MutablePropertySources::addLast, false));
					this.processedProfiles.add(profile);
				}
				//未指定profile加载
				load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
				addLoadedPropertySources();
				applyActiveProfiles(defaultProperties);
			});
}
//org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load(org.springframework.boot.context.config.ConfigFileApplicationListener.Profile, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentFilterFactory, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentConsumer)
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
    
    
	getSearchLocations().forEach((location) -> {
    
    
		boolean isDirectory = location.endsWith("/");
		Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
		//继续走load
		names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
	});
}
//org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load(java.lang.String, java.lang.String, org.springframework.boot.context.config.ConfigFileApplicationListener.Profile, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentFilterFactory, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentConsumer)
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
		DocumentConsumer consumer) {
    
    
	if (!StringUtils.hasText(name)) {
    
    
		for (PropertySourceLoader loader : this.propertySourceLoaders) {
    
    
			if (canLoadFileExtension(loader, location)) {
    
    
				load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
				return;
			}
		}
		throw new IllegalStateException("File extension of config file location '" + location
				+ "' is not known to any PropertySourceLoader. If the location is meant to reference "
				+ "a directory, it must end in '/'");
	}
	Set<String> processed = new HashSet<>();
	for (PropertySourceLoader loader : this.propertySourceLoaders) {
    
    
		for (String fileExtension : loader.getFileExtensions()) {
    
    
			//fileExtension :文件的扩展名
			if (processed.add(fileExtension)) {
    
    
				loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
						consumer);
			}
		}
	}
}

DEBUG:
在这里插入图片描述

//org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#loadForFileExtension
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension,
		Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
    
    
	DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
	DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
	if (profile != null) {
    
    
		// Try profile-specific file & profile section in profile file (gh-340)
		String profileSpecificFile = prefix + "-" + profile + fileExtension;
		load(loader, profileSpecificFile, profile, defaultFilter, consumer);
		load(loader, profileSpecificFile, profile, profileFilter, consumer);
		// Try profile specific sections in files we've already processed
		for (Profile processedProfile : this.processedProfiles) {
    
    
			if (processedProfile != null) {
    
    
				String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
				load(loader, previouslyLoaded, profile, profileFilter, consumer);
			}
		}
	}
	// Also try the profile-specific section (if any) of the normal file
	// 走这
	load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}
//org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#load(org.springframework.boot.env.PropertySourceLoader, java.lang.String, org.springframework.boot.context.config.ConfigFileApplicationListener.Profile, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentFilter, org.springframework.boot.context.config.ConfigFileApplicationListener.DocumentConsumer)
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
		DocumentConsumer consumer) {
    
    
	Resource[] resources = getResources(location);
	for (Resource resource : resources) {
    
    
		try {
    
    
			if (resource == null || !resource.exists()) {
    
    
				if (this.logger.isTraceEnabled()) {
    
    
					StringBuilder description = getDescription("Skipped missing config ", location, resource,
							profile);
					this.logger.trace(description);
				}
				continue;
			}
			if (!StringUtils.hasText(StringUtils.getFilenameExtension(resource.getFilename()))) {
    
    
				if (this.logger.isTraceEnabled()) {
    
    
					StringBuilder description = getDescription("Skipped empty config extension ", location,
							resource, profile);
					this.logger.trace(description);
				}
				continue;
			}
			String name = "applicationConfig: [" + getLocationName(location, resource) + "]";
			//加载文档
			List<Document> documents = loadDocuments(loader, name, resource);
			if (CollectionUtils.isEmpty(documents)) {
    
    
				if (this.logger.isTraceEnabled()) {
    
    
					StringBuilder description = getDescription("Skipped unloaded config ", location, resource,
							profile);
					this.logger.trace(description);
				}
				continue;
			}
			List<Document> loaded = new ArrayList<>();
			for (Document document : documents) {
    
    
				if (filter.match(document)) {
    
    
					addActiveProfiles(document.getActiveProfiles());
					addIncludedProfiles(document.getIncludeProfiles());
					loaded.add(document);
				}
			}
			Collections.reverse(loaded);
			if (!loaded.isEmpty()) {
    
    
				loaded.forEach((document) -> consumer.accept(profile, document));
				if (this.logger.isDebugEnabled()) {
    
    
					StringBuilder description = getDescription("Loaded config file ", location, resource,
							profile);
					this.logger.debug(description);
				}
			}
		}
		catch (Exception ex) {
    
    
			StringBuilder description = getDescription("Failed to load property source from ", location,
					resource, profile);
			throw new IllegalStateException(description.toString(), ex);
		}
	}
}
//org.springframework.boot.context.config.ConfigFileApplicationListener.Loader#loadDocuments
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);
		documents = asDocuments(loaded);
		this.loadDocumentsCache.put(cacheKey, documents);
	}
	return documents;
}

DEBUG:
在这里插入图片描述

public class YamlPropertySourceLoader implements PropertySourceLoader {
    
    

	@Override
	public String[] getFileExtensions() {
    
    
		return new String[] {
    
     "yml", "yaml" };
	}

	@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();
		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;
	}

}

DEBUG
在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_41947378/article/details/108720152