springboot 读取application.properties流程

一、application.properties配置如下,当然也可以配置YAML。

application-dev.properties

server.port=8110
spring.application.name=newday-service


spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace

spring.datasource.primary.jdbc-url=jdbc:mysql://rm-wz96s5izji5099uz13o.mysql.rds.aliyuncs.com:3306/xfz178_com?serverTimezone=UTC&characterEncoding=utf8


## test2 database
#spring.datasource.localdb.jdbc-url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf8
#spring.datasource.localdb.username=root
#spring.datasource.localdb.password=123456
#spring.datasource.localdb.driverClassName=com.mysql.cj.jdbc.Driver



#logging.file.name=./logs/newday-service.log
#日志级别
#logging.file.level.ROOT=DEBUG
#logging.config=classpath:log4j2.xml
logging.config=classpath:logback-spring.xml

redisson.address = redis://127.0.0.1:6379
redisson.password =


#people.beanfactory = PeopleSpringFactory
people.beanfactory = PeopleBeanFactory

 二、加载流程

   1.SpringApplication在Run时,会先预加载环境,即是读取各种配置到环境对象中,然后会广播

ApplicationEnvironmentPreparedEvent消息。
org.springframework.boot.context.event.environmentPrepared
	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster
				.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
	}

2.org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEvent会被触发调用,因为其实现了ApplicationListener会接收事件。

	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ApplicationEnvironmentPreparedEvent) {
			onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
		}
		if (event instanceof ApplicationPreparedEvent) {
			onApplicationPreparedEvent(event);
		}
	}

 3.然后会调用到ConfigFileApplicationListener.postProcessEnvironment,去加载属性配置文件到环境对象。

public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
		addPropertySources(environment, application.getResourceLoader());
	}

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
		RandomValuePropertySource.addToEnvironment(environment);
		new Loader(environment, resourceLoader).load();
	}

4.在ConfigFileApplicationListener.Loader的内部类中,会初始化两个资源配置文件加载器。properties,yaml

 5.在loader.load方法中会初始化所有的profile,然后再搜索可用的PROFILE配置文件。

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

6. 第二个load方法

		private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
			getSearchLocations().forEach((location) -> {
				boolean isDirectory = location.endsWith("/");
				Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
				names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
			});
		}

最终读取到的搜索路径为:

 获取搜索文件名,这里注意,如果环境对象中有CONFIG_NAME_PROPERT(spring.config.name)这个配置属性,前面的bootStrap.properties就是通过前一个BootstrapApplicationListener加载的listener,这个对象会在监听到环境准备事件时,设置spring.config.name为bootStrap,然后由当前这个listener来加载配置,则搜索这个文件名,否则搜索默认文件名DEFAULT_NAMES(application):

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

7.接着调用第三个load方法,搜索不同目录下的配置文件。

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

8.接着调用第四个load,加载指定扩展名的文件。

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

9.接着调用第五个load方法,真正去目录搜索指定文件名和后缀的文件,然后加载到文档。

	private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
				DocumentConsumer consumer) {
			Resource[] resources = getResources(location);
			for (Resource resource : resources) {
				try {
					String name = "applicationConfig: [" + getLocationName(location, resource) + "]";
					List<Document> documents = loadDocuments(loader, name, resource);
				
					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);
						}
					}
				}
				
			}
		}

10.在加载文档时最后会转换文档 。

		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);
				String[] profiles = binder.bind("spring.profiles", STRING_ARRAY).orElse(null);
				Set<Profile> activeProfiles = getProfiles(binder, ACTIVE_PROFILES_PROPERTY);
				Set<Profile> includeProfiles = getProfiles(binder, INCLUDE_PROFILES_PROPERTY);
				return new Document(propertySource, profiles, activeProfiles, includeProfiles);
			}).collect(Collectors.toList());
		}

11.getProfiles(binder, ACTIVE_PROFILES_PROPERTY)这句话,会去从当前读取到的配置文件属性中查找spring.profiles.active属性,然后设置到环境变量。
        这个绑定方法和@configurationProperties中的对象属性绑定原理一致,都是从资源属性列表中逐个资源文件中搜索名称为name的配置。像这里就是spring.profile.active,spring.profile.include

		
private Set<Profile> getProfiles(Binder binder, String name) {

			return binder.bind(name, STRING_ARRAY).map(this::asProfileSet).orElse(Collections.emptySet());
		}

 注意这里的binder不是当前环境变量的binder,而是当前搜索到的配置文件动态生成的BINDER.

Binder binder = new Binder(ConfigurationPropertySources.from(propertySource),
      this.placeholdersResolver);

 现在我们读取application.properties的配置数据了,只有一个。

 12.然后我们接着最后一个load代码,当读取到默认的配置文档后,会再次加载激活和包含的profile配置。

addActiveProfiles(document.getActiveProfiles());
							addIncludedProfiles(document.getIncludeProfiles());
							loaded.add(document);
	void addActiveProfiles(Set<Profile> profiles) {
			this.profiles.addAll(profiles);

			this.activatedProfiles = true;
			removeUnprocessedDefaultProfiles();
		}

此时我们可以看到,已经把dev的profile也加进来了。

 13.最后会把加载到的文档 配置属性回调给消费者函数。

loaded.forEach((document) -> consumer.accept(profile, document));
ConfigFileApplicationListener.addToLoaded就是消费的回调。
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());
			};
		}

这里面的addMethod又是一个回调方法,最终调用到

MutablePropertySources.addLast将这个资源文件对象加载到环境对象的propertList属性中。

设置两个回调的地方在第一个load中,可以找到第一个看代码。

load(profile, this::getPositiveProfileFilter,
      addToLoaded(MutablePropertySources::addLast, false));
	public void addLast(PropertySource<?> propertySource) {
		synchronized (this.propertySourceList) {
			removeIfPresent(propertySource);
			this.propertySourceList.add(propertySource);
		}
	}

 最终属性数据存储在内部类loader

private Map<Profile, MutablePropertySources> loaded 属性中。

14.然后我们再回到第一个load函数,他会不断循环所有的profiles,因为之前已经往这个profile列表中加上dev,所以会再次循环加载dev的profile配置,流程一样。

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

 这是加载dev的profile的配置属性列表。

 然后回调,就会把dev的文档生成资源属性文件添加到loaded列表。

 15.最后会调用addLoadedPropertySources把加载成功的配置属性添加到环境对象的propertySources中。

	private void addLoadedPropertySources() {
			MutablePropertySources destination = this.environment.getPropertySources();
			List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
			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();
					}
				}
			}
		}

加载前

 加载后

我们可以看到,是加载到尾部,这也证明了配置文件的优先级最低。同时我们可以看到dev的配置文件是在默认之前。所以会覆盖默认的。

注意上面添加代码,有对loaded作倒序处理。

Collections.reverse(loaded);

至此,配置文件属性加载完毕。 

Spring Boot读取application.properties的值的方式有多种方法。一种常用的方式是通过使用@Value注解来注入属性值。可以在需要使用属性值的字段、方法的参数、方法的返回值上添加@Value注解,并指定要注入的属性的键名。例如,可以使用@Value("${spring.rabbitmq.host}")来注入application.properties文件中的spring.rabbitmq.host属性的值。这样,在程序运行时,Spring Boot会自动读取属性文件中的值,并将其注入到相应的位置。 另一种方式是使用PropertiesLoaderUtils类来读取属性文件的值。首先,需要导入java.util.Properties和org.springframework.core.io.support.PropertiesLoaderUtils类。然后,可以使用PropertiesLoaderUtils的loadProperties方法来加载属性文件,然后使用getProperty方法获取具体的属性值。例如,可以使用Properties properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource("application.properties"))来加载application.properties文件,然后使用properties.getProperty("spring.rabbitmq.host")来获取spring.rabbitmq.host属性的值。 总结起来,Spring Boot提供了多种方式来读取application.properties文件的值,可以根据具体的需求选择适合的方式来获取属性值。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Spring Boot中配置文件application.properties使用](https://download.csdn.net/download/weixin_38672800/12765190)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [SpringBoot获得application.properties中数据的几种方式](https://blog.csdn.net/m0_67393686/article/details/124421983)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [SpringBoot读取application.properties配置文件](https://blog.csdn.net/watson2017/article/details/124732267)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值