springboot加载properties文件过程详解

参考文档
SpringBoot配置文件的加载位置实例详解
通过源码了解springboot加载application.properties过程
从SpringBoot源码分析 配置文件的加载原理和优先级

总结
spring.config.location 加载配置文件的路径
没有配置默认值为 classpath:/,classpath:/config/,file:./,file:./config/

spring.config.name 加载配置文件的文件名
没有配置默认值为 application

spring.config.additional-location 加载配置文件的额外路径
没有配置spring.config.location 才加载额外路径

加载配置文件
location+name+[-profile]+fileExtension location为路径
一定会加载 profile 为 null的情况

获取配置文件中的属性优先级
1 配置了 spring.profiles.active=dev,test 配置的顺序倒叙优先
2 文件位置 location classpath:/,classpath:/config/,file:./,file:./config/ 倒叙优先

文章目录

    • ConfigFileApplicationListener 类关键属性
    • ConfigFileApplicationListener 监听事件类型
    • springboot 广播监听的触发时机
    • 触发onApplicationEvent
    • onApplicationEnvironmentPreparedEvent
    • postProcessEnvironment
    • Loader.load 过程
      • 关键类
        • Loader
        • Document
      • load
        • 初始化Profiles initializeProfiles
        • load (Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer)
          • 创建 DocumentFilter
          • 创建 DocumentConsumer
        • 获取加载配置文件路径 getSearchLocations
        • 获取加载配置文件 文件名 getSearchNames
        • 根据文件路径和文件名加载配置
        • loadForFileExtension
        • load(PropertySourceLoader loader, String location, Profile profile,DocumentFilter filter, DocumentConsumer consumer)
        • loadDocuments
        • 根据PropertySource创建Document
        • addLoadedPropertySources 将加载的PropertySource 添加到Environment

ConfigFileApplicationListener 类关键属性

public class ConfigFileApplicationListener
		implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {


	private static final String DEFAULT_PROPERTIES = "defaultProperties";

	// Note the order is from least to most specific (last one wins)
	// springboot 加载配置文件的默认位置
	private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";

	private static final String DEFAULT_NAMES = "application";

	/**
	 * The "active profiles" property name.
	 */
	public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";

	/**
	 * The "includes profiles" property name.
	 */
	public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";

	/**
	 * The "config name" property name.
	 */
	public static final String CONFIG_NAME_PROPERTY = "spring.config.name";

	/**
	 * The "config location" property name.
	 */
	public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";

	/**
	 * The "config additional location" property name.
	 */
	public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";




}

ConfigFileApplicationListener 监听事件类型

ApplicationEnvironmentPreparedEvent
ApplicationPreparedEvent

springboot 广播监听的触发时机

//springboot run方法
org.springframework.boot.SpringApplication#run(java.lang.String...)
// 准备 ConfigurableEnvironment 
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);

//广播ApplicationEnvironmentPreparedEvent事件
listeners.environmentPrepared(environment);
//加载配置文件监听执行
ConfigFileApplicationListener 

触发onApplicationEvent

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		// 监听 ApplicationEnvironmentPreparedEvent
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent(
					(ApplicationEnvironmentPreparedEvent) event);
		}
		// 监听ApplicationPreparedEvent 
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent(event);
		}
	}

onApplicationEnvironmentPreparedEvent

	private void onApplicationEnvironmentPreparedEvent(
			ApplicationEnvironmentPreparedEvent event) {
		// 加载META-INF/spring.factories下的EnvironmentPostProcessor 
		// springboot默认有三个实现
		List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
		//加入ConfigFileApplicationListener 也实现了EnvironmentPostProcessor
		postProcessors.add(this);
		AnnotationAwareOrderComparator.sort(postProcessors);
		for (EnvironmentPostProcessor postProcessor : postProcessors) {
			// 执行 EnvironmentPostProcessor的 postProcessEnvironment(ConfigurableEnvironment environment,SpringApplication application) 方法
			postProcessor.postProcessEnvironment(event.getEnvironment(),
					event.getSpringApplication());
		}
	}

META-INF/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

postProcessEnvironment

postProcessEnvironment

	public void postProcessEnvironment(ConfigurableEnvironment environment,
			SpringApplication application) {
		//默认情况下 application.getResourceLoader()为空
		addPropertySources(environment, application.getResourceLoader());
	}

addPropertySources

	protected void addPropertySources(ConfigurableEnvironment environment,
			ResourceLoader resourceLoader) {
		// 添加 systemEnvironment RandomValuePropertySource
		RandomValuePropertySource.addToEnvironment(environment);
		//加载配置文件
		new Loader(environment, resourceLoader).load();
	}

Loader.load 过程

springboot加载properties文件过程详解_第1张图片

关键类

Loader

private class Loader {

		private final ConfigurableEnvironment environment;

		private final PropertySourcesPlaceholdersResolver placeholdersResolver;

		private final ResourceLoader resourceLoader;
		// 
		private final List<PropertySourceLoader> propertySourceLoaders;

		private Deque<Profile> profiles;

		private List<Profile> processedProfiles;

		private boolean activatedProfiles;
		//存储 MutablePropertySources
		private Map<Profile, MutablePropertySources> loaded;

		private Map<DocumentsCacheKey, List<Document>> loadDocumentsCache = new HashMap<>();

		Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
			// 赋值 environment
			this.environment = environment;
			this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(
					this.environment);
			// resourceLoader 为空, new DefaultResourceLoader()
			this.resourceLoader = (resourceLoader != null) ? resourceLoader
					: new DefaultResourceLoader();
			// 加载 META-INF/spring.factories PropertySourceLoader 两个
			// org.springframework.boot.env.PropertiesPropertySourceLoader
			// org.springframework.boot.env.YamlPropertySourceLoader
			this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
					PropertySourceLoader.class, getClass().getClassLoader());
		}
}

Document

private static class Document {
		// 读取配置文件结果 PropertySource
		private final PropertySource<?> propertySource;

		private String[] profiles;

		private final Set<Profile> activeProfiles;

		private final Set<Profile> includeProfiles;

		Document(PropertySource<?> propertySource, String[] profiles,
				Set<Profile> activeProfiles, Set<Profile> includeProfiles) {
			this.propertySource = propertySource;
			this.profiles = profiles;
			this.activeProfiles = activeProfiles;
			this.includeProfiles = includeProfiles;
		}
}

load

public void load() {
			this.profiles = new LinkedList<>();
			this.processedProfiles = new LinkedList<>();
			this.activatedProfiles = false;
			this.loaded = new LinkedHashMap<>();
			// 初始化Profile
			initializeProfiles();
			while (!this.profiles.isEmpty()) {
				Profile profile = this.profiles.poll();
				if (profile != null && !profile.isDefaultProfile()) {
					//将profile添加到Environment activeProfiles
					addProfileToEnvironment(profile.getName());
				}
				// DocumentFilterFactory this::getPositiveProfileFilter 方法引用
				// DocumentConsumer addToLoaded 创建对象
				load(profile, this::getPositiveProfileFilter,
						addToLoaded(MutablePropertySources::addLast, false));
				// 标记已经处理过的profile
				this.processedProfiles.add(profile);
			}
			// 重置Environment  activeProfiles 
			resetEnvironmentProfiles(this.processedProfiles);
			load(null, this::getNegativeProfileFilter,
					addToLoaded(MutablePropertySources::addFirst, true));
		    //load Map 到 Environment
			addLoadedPropertySources();
		}

初始化Profiles initializeProfiles

默认添加 null Profile
如果环境变量没有设置spring.profiles.active 添加 DefaultProfiles default
如果设置,添加已经设置的spring.profiles.active 作为Profile
未初始化 设置 activatedProfiles=true ,以后不再添加

		private void initializeProfiles() {
			// The default profile for these purposes is represented as null. We add it
			// first so that it is processed first and has lowest priority.
			// 添加 null profile
			this.profiles.add(null);
			// 从 Environment 属性中获取 spring.profiles.active 作为Profile
			Set<Profile> activatedViaProperty = getProfilesActivatedViaProperty();
			// 添加 Environment 已存在的 activeProfiles
			this.profiles.addAll(getOtherActiveProfiles(activatedViaProperty));
			// Any pre-existing active profiles set via property sources (e.g.
			// System properties) take precedence over those added in config files.
			// 添加profiles到 Load#profiles 并设置 activatedProfiles=true
			// 当activatedProfiles为true直接返回
			addActiveProfiles(activatedViaProperty);
			// 只有null 环境中未设置spring.profiles.active
			if (this.profiles.size() == 1) { // only has null profile
				// 将environment默认spring.profiles.default添加到 Load#profiles
				for (String defaultProfileName : this.environment.getDefaultProfiles()) 
					Profile defaultProfile = new Profile(defaultProfileName, true);
					this.profiles.add(defaultProfile);
				}
			}
		}

load (Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer)

类接口图
springboot加载properties文件过程详解_第2张图片

filterFactory 是getPositiveProfileFilter的方法引用
consumer addToLoaded创建的对象 对象中传入 MutablePropertySources::addLast 方法引用

		private void load(Profile profile, DocumentFilterFactory filterFactory,
				DocumentConsumer consumer) {
			// 获取加载配置文件路径 location
			getSearchLocations().forEach((location) -> {
				boolean isFolder = location.endsWith("/");
				// 如果是目录 获取文件名 
				Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
				names.forEach(
				//根据文件路径,文件名,Profile filterFactory,consumer 加载配置
				(name) -> load(location, name, profile, filterFactory, consumer));
			});
		}
创建 DocumentFilter
	private DocumentFilter getPositiveProfileFilter(Profile profile) {
		return (Document document) -> {
			if (profile == null) {
				return ObjectUtils.isEmpty(document.getProfiles());
			}
			return ObjectUtils.containsElement(document.getProfiles(),
					profile.getName())
					&& this.environment
							.acceptsProfiles(Profiles.of(document.getProfiles()));
		};
	}
创建 DocumentConsumer

addToLoaded
传入addFirst 方法引用
addToLoaded(MutablePropertySources::addFirst, true)

		private DocumentConsumer addToLoaded(
				BiConsumer<MutablePropertySources, PropertySource<?>> addMethod,
				boolean checkForExisting) {
			return (profile, document) -> {
				if (checkForExisting) {
					for (MutablePropertySources merged : this.loaded.values()) {
						if (merged.contains(document.getPropertySource().getName())) {
							return;
						}
					}
				}
				MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
						(k) -> new MutablePropertySources());
				addMethod.accept(merged, document.getPropertySource());
			};
		}

获取加载配置文件路径 getSearchLocations

1 环境中配置了 spring.config.location
取 spring.config.location配置的值,多个以逗号隔开
2 环境中没有配置 spring.config.location
取环境中配置的 spring.config.additional-location (如果有)
取默认的配置路径 classpath:/,classpath:/config/,file:./,file:./config/

		private Set<String> getSearchLocations() {
			if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
				return getSearchLocations(CONFIG_LOCATION_PROPERTY);
			}
			Set<String> locations = getSearchLocations(
					CONFIG_ADDITIONAL_LOCATION_PROPERTY);
			locations.addAll(
					asResolvedSet(ConfigFileApplicationListener.this.searchLocations,
							DEFAULT_SEARCH_LOCATIONS));
			return locations;
		}

获取加载配置文件 文件名 getSearchNames

1 环境中配置了spring.config.name
取spring.config.name 配置的值,多个以逗号隔开
2 环境中没有配置 spring.config.name
取默认的文件名 application

		private Set<String> getSearchNames() {
			if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
				String property = this.environment.getProperty(CONFIG_NAME_PROPERTY);
				return asResolvedSet(property, null);
			}
			return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
		}

根据文件路径和文件名加载配置

		private void load(String location, String name, Profile profile,
				DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
			// 文件名为空
			if (!StringUtils.hasText(name)) {
				for (PropertySourceLoader loader : this.propertySourceLoaders) {
				    // location文件 PropertySourceLoader 是否支持
					if (canLoadFileExtension(loader, location)) {
						load(loader, location, profile,
								filterFactory.getDocumentFilter(profile), consumer);
						return;
					}
				}
			}
			// 文件名不为空
			Set<String> processed = new HashSet<>();
		    // org.springframework.boot.env.PropertiesPropertySourceLoader
			// org.springframework.boot.env.YamlPropertySourceLoader
			for (PropertySourceLoader loader : this.propertySourceLoaders) {
				// 获取PropertySourceLoader支持的文件
				for (String fileExtension : loader.getFileExtensions()) {
					if (processed.add(fileExtension)) {
						loadForFileExtension(loader, location + name, "." + fileExtension,
								profile, filterFactory, consumer);
					}
				}
			}
		}

loadForFileExtension

prefix 文件前缀 location + name
fileExtension PropertySourceLoader 支持的拓展名

private void loadForFileExtension(PropertySourceLoader loader, String prefix,
				String fileExtension, Profile profile,
				DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
			// 空的profile
			DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
			DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
			// profile != null 情况
			if (profile != null) {
				// Try profile-specific file & profile section in profile file (gh-340)
				String profileSpecificFile = prefix + "-" + profile + fileExtension;
				// 根据DocumentFilter 过滤加载配置 
				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);
		}

load(PropertySourceLoader loader, String location, Profile profile,DocumentFilter filter, DocumentConsumer consumer)

private void load(PropertySourceLoader loader, String location, Profile profile,
				DocumentFilter filter, DocumentConsumer consumer) {
			try {
				Resource resource = this.resourceLoader.getResource(location);
				if (resource == null || !resource.exists()) {
					if (this.logger.isTraceEnabled()) {
						StringBuilder description = getDescription(
								"Skipped missing config ", location, resource, profile);
						this.logger.trace(description);
					}
					return;
				}
				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);
					}
					return;
				}
				String name = "applicationConfig: [" + location + "]";
				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);
					}
					return;
				}
				List<Document> loaded = new ArrayList<>();
				for (Document document : documents) {
					// 分级 profile过滤
					if (filter.match(document)) {
						// 将document.getActiveProfiles() 添加到 this.profiles
						// 只添加一次 activatedProfiles 为true不再添加
						addActiveProfiles(document.getActiveProfiles());
						addIncludedProfiles(document.getIncludeProfiles());
						loaded.add(document);
					}
				}
				Collections.reverse(loaded);
				if (!loaded.isEmpty()) {
				    // 添加资源到 load中
				    // addToLoaded
					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) {
				throw new IllegalStateException("Failed to load property "
						+ "source from location '" + location + "'", ex);
			}
		}

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) {
			   // new OriginTrackedMapPropertySource(name, properties)
				List<PropertySource<?>> loaded = loader.load(name, resource);
				documents = asDocuments(loaded);
				this.loadDocumentsCache.put(cacheKey, documents);
			}
			return documents;
		}

根据PropertySource创建Document

		private List<Document> asDocuments(List<PropertySource<?>> loaded) {
			if (loaded == null) {
				return Collections.emptyList();
			}
			return loaded.stream().map((propertySource) -> {
				Binder binder = new Binder(
						ConfigurationPropertySources.from(propertySource),
						this.placeholdersResolver);
				return new Document(propertySource,
						binder.bind("spring.profiles", STRING_ARRAY).orElse(null),
						//PropertySource中的 spring.profiles.active作为 Document activeProfiles 
						getProfiles(binder, ACTIVE_PROFILES_PROPERTY),
					   //PropertySource中的 spring.profiles.include 作为 Document activeProfiles 
						getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
			}).collect(Collectors.toList());
		}

addLoadedPropertySources 将加载的PropertySource 添加到Environment

		private void addLoadedPropertySources() {
			MutablePropertySources destination = this.environment.getPropertySources();
			List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
			// 倒序 后加入的Profile优先加入  即设置的spring.profiles.active优先于 null
			Collections.reverse(loaded);
			String lastAdded = null;
			Set<String> added = new HashSet<>();
			for (MutablePropertySources sources : loaded) {
				for (PropertySource<?> source : sources) {
					if (added.add(source.getName())) {
						// 按照集合先后顺序添加
						addLoadedPropertySource(destination, lastAdded, source);
						lastAdded = source.getName();
					}
				}
			}
		}

你可能感兴趣的:(spring-boot)