SpringBoot 2.4.4 Environment

博文目录


SpringBoot源码系列:Environment机制深入分析(一)
SpringBoot源码系列:Environment机制深入分析(二)
关于Spring属性处理器PropertyResolver以及应用运行环境Environment的深度分析,强大的StringValueResolver使用和解析

入口

在SpringBoot启动的 SpringApplication.run() 方法中会初始化一个匹配应用类型的Environment对象, 用来管理应用中的各种 profiles 和 properties, 下面就是Environment初始化的入口, 从源码层面死磕一下原理

// org.springframework.boot.SpringApplication#run(java.lang.String...)
public ConfigurableApplicationContext run(String... args) {
	// ...
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
		configureIgnoreBeanInfo(environment);
		// ...
	} catch (Throwable ex) {}
	// ...
}
// 构造一个包含了各种属性源并按优先级排好顺序的Environment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
		DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
	
	// 根据 webApplicationType 创建 Environment
	// 内置 servletConfigInitParams > servletContextInitParams > systemProperties > systemEnvironment 4个属性源(有时候可能有5个)
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	
	// 将命令行参数读取到Environment中
	// 如果有启动参数(如:java -jar xxx.jar --key value), 则尝试在Environment中补充SimpleCommandLinePropertySource类型的commandLineArgs属性源, 并放在首位(最高优先级)
	// 如果已经存在commandLineArgs属性源了, 则将其转成CompositePropertySource(组合属性源,这种源内可以包含多个其他源), 然后再将命令行参数封装成SimpleCommandLinePropertySource类型的springApplicationCommandLineArgs也添加进去, 最后用组合源替换掉同名的原始源
	// 属性源顺序 commandLineArgs > servletConfigInitParams > servletContextInitParams > systemProperties > systemEnvironment
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	
	// 将当前Environment的PropertySources封装成ConfigurationPropertySourcesPropertySource类型的configurationProperties, 并添加到PropertySources的最前面, 搞成一个无限循环, 不知道这个操作是要干嘛
	// 属性源顺序 configurationProperties > commandLineArgs > servletConfigInitParams > servletContextInitParams > systemProperties > systemEnvironment
	ConfigurationPropertySources.attach(environment);
	
	// 发布了ApplicationEnvironmentPreparedEvent 事件, 在监听器中读取了全局配置文件并添加到Environment中
	// 属性源顺序 configurationProperties > commandLineArgs > servletConfigInitParams > servletContextInitParams > systemProperties > systemEnvironment > random > application.properties > defaultProperties > devtools
	// application.properties 的name比较奇怪, Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'
	listeners.environmentPrepared(bootstrapContext, environment);
	
	// 将名为defaultProperties的属性源(存在的话)挪到最后, 即降低其优先级
	// 属性源顺序 configurationProperties > commandLineArgs > servletConfigInitParams > servletContextInitParams > systemProperties > systemEnvironment > random > application.properties > devtools > defaultProperties
	DefaultPropertiesPropertySource.moveToEnd(environment);
	
	// 补充额外的激活的profiles
	configureAdditionalProfiles(environment);
	
	// 将所有spring.main 开头的配置信息绑定SpringApplication
	// Binder的使用其实比较简单, 有点类似注解@ConfigurationProperties的作用,都是将属性绑定到某个具体的对象上。 但是有一点区别 @ConfigurationProperties是在容器启动时绑定的,而Binder是我们手动编码动态的绑定上去的
	// 如:sources ,bannerMode等属性赋值给当前的对象。也就是将spring.main.sources 绑定到SpringbootApplication的sources属性上 将spring.main.banner-mode绑定到bannerMode属性上。可以理解为将属性动态绑定到对象上。
	bindToSpringApplication(environment);
	
	// 如果Evnrionment是由构建SpringApplication的SpringApplicationBuilder中set进来的, 则就是自定义的, isCustomEnvironment的值就是true, 这里值为false的时候才会进入条件
	if (!this.isCustomEnvironment) {
		// 根据应用类型推断Environment本应该的类型, 与当前Environment的类型对比, 如果匹配就算了, 不匹配则转化成StandardEnvironment
		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
				deduceEnvironmentClass());
	}
	
	// 判断第一位的属性源是不是还是持有PropertySources的configurationProperties属性源, 是就算了, 不是则移除重新添加
	ConfigurationPropertySources.attach(environment);
	
	return environment;
}
// SpringApplication.run()方法在前面会判断应用类型, 可能是 SERVLET/REACTIVE/NONE, 根据类型创建不同的Environment对象
// 现阶段我们的SpringBoot应用通常是SERVLET, 这里我们看 StandardServletEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() {
	if (this.environment != null) {
		return this.environment;
	}
	switch (this.webApplicationType) {
	case SERVLET:
		// 这里使用的是无参构造函数
		return new StandardServletEnvironment();
	case REACTIVE:
		return new StandardReactiveWebEnvironment();
	default:
		return new StandardEnvironment();
	}
}

着重说明一下 listeners.environmentPrepared(bootstrapContext, environment); 环境准备事件的发布

listeners的来源

// SpringApplication#run(java.lang.String...)
SpringApplicationRunListeners listeners = getRunListeners(args);

private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
	// new了一个SpringApplicationRunListeners, 第二个参数是从 spring.factories 中找到的SpringApplicationRunListener, 只有一个, 就是 EventPublishingRunListener
	// 所以, SpringApplicationRunListeners的listeners里面只有一个EventPublishingRunListener对象
	return new SpringApplicationRunListeners(logger,
			getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
			this.applicationStartup);
}

listeners的使用

// SpringApplication#prepareEnvironment
listeners.environmentPrepared(bootstrapContext, environment);

void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
	// 传入一个 消耗listener的consumer接口函数
	doWithListeners("spring.boot.application.environment-prepared",
			(listener) -> listener.environmentPrepared(bootstrapContext, environment));
}

private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
	doWithListeners(stepName, listenerAction, null);
}

private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
		Consumer<StartupStep> stepAction) {
	StartupStep step = this.applicationStartup.start(stepName);
	// 遍历 SpringApplicationRunListeners的listeners, 每个listener都执行一下其environmentPrepared方法
	// 这里的listener只有一个EventPublishingRunListener对象
	this.listeners.forEach(listenerAction);
	if (stepAction != null) {
		stepAction.accept(step);
	}
	step.end();
}
// EventPublishingRunListener的构造函数, 初始化多播器, 将SpringApplication中的listener全都绑定到多播器上
// 使用多播器发布事件的时候, 将会通知到所有的listener上, 完成其他的流程
public EventPublishingRunListener(SpringApplication application, String[] args) {
	this.application = application;
	this.args = args;
	this.initialMulticaster = new SimpleApplicationEventMulticaster();
	for (ApplicationListener<?> listener : application.getListeners()) {
		this.initialMulticaster.addApplicationListener(listener);
	}
}

// EventPublishingRunListener.environmentPrepared, 发布事件
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
	// initialMulticaster属性在初始化的时候会把SpringApplication里面的listener全都添加到自己的listener里面
	this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
// SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent)
public void multicastEvent(ApplicationEvent event) {
	multicastEvent(event, resolveDefaultEventType(event));
}

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	Executor executor = getTaskExecutor();
	// 尽早将能处理该事件的listener过滤出来, 实测有些不能处理的也被拿过来了
	// 因为调用无参构造器初始化的, 所以executor是null, 所以走单线程调用
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
			// 单线程调用listener
			invokeListener(listener, event);
		}
	}
}

实测getApplicationListeners(event, type)拿到的参与遍历的ApplicationListener, 逐个调用其onApplicationEvent方法, 下面仅列出能响应ApplicationEnvironmentPreparedEvent事件的监听器的执行流程

EnvironmentPostProcessorApplicationListener

// EnvironmentPostProcessorApplicationListener#onApplicationEvent
public void onApplicationEvent(ApplicationEvent event) {
	// 条件为true, 走流程了
	if (event instanceof ApplicationEnvironmentPreparedEvent) {
		onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
	}
	if (event instanceof ApplicationPreparedEvent) {
		onApplicationPreparedEvent((ApplicationPreparedEvent) event);
	}
	if (event instanceof ApplicationFailedEvent) {
		onApplicationFailedEvent((ApplicationFailedEvent) event);
	}
}

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
	ConfigurableEnvironment environment = event.getEnvironment();
	SpringApplication application = event.getSpringApplication();
	// 拿了一堆EnvironmentPostProcessor对象出来, 逐个调用其postProcessEnvironment方法
	for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(event.getBootstrapContext())) {
		postProcessor.postProcessEnvironment(environment, application);
	}
}

下面是各个EnvironmentPostProcessor的执行流程

RandomValuePropertySourceEnvironmentPostProcessor

// RandomValuePropertySourceEnvironmentPostProcessor#postProcessEnvironment
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
	// 判断environment中是否有systemEnvironment的属性源, 有的话在其后添加random属性源, 没有的话在最后添加random属性源
	RandomValuePropertySource.addToEnvironment(environment, this.logger);
}

SystemEnvironmentPropertySourceEnvironmentPostProcessor

// SystemEnvironmentPropertySourceEnvironmentPostProcessor#postProcessEnvironment
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
	String sourceName = StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
	PropertySource<?> propertySource = environment.getPropertySources().get(sourceName);
	if (propertySource != null) {
		replacePropertySource(environment, sourceName, propertySource);
	}
}

private void replacePropertySource(ConfigurableEnvironment environment, String sourceName, PropertySource<?> propertySource) {
	Map<String, Object> originalSource = (Map<String, Object>) propertySource.getSource();
	// 把原本的SystemEnvironmentPropertySource类型的systemEnvironment属性源重新包装成OriginAwareSystemEnvironmentPropertySource类型
	SystemEnvironmentPropertySource source = new OriginAwareSystemEnvironmentPropertySource(sourceName, originalSource);
	// 把旧的替换一下
	environment.getPropertySources().replace(sourceName, source);
}

SpringApplicationJsonEnvironmentPostProcessor 暂不关心

// SpringApplicationJsonEnvironmentPostProcessor#postProcessEnvironment
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
	MutablePropertySources propertySources = environment.getPropertySources();
	// 遍历全部属性源, 从每一个属性源中拿 spring.application.json/SPRING_APPLICATION_JSON 属性的值
	// 如果值是String且不是空串, 就构建一个JsonPropertyValue对象, 否则就返回null, 然后拿到第一个非空的
	// 如果存在非空的, 则对其执行processJson方法
	// JsonPropertyValue中含有存在spring.application.json/SPRING_APPLICATION_JSON属性的属性源, key, value
	propertySources.stream().map(JsonPropertyValue::get).filter(Objects::nonNull).findFirst()
			.ifPresent((v) -> processJson(environment, v));
}

private void processJson(ConfigurableEnvironment environment, JsonPropertyValue propertyValue) {
	JsonParser parser = JsonParserFactory.getJsonParser();
	Map<String, Object> map = parser.parseMap(propertyValue.getJson());
	if (!map.isEmpty()) {
		// 按jndiProperties>servletContextInitParams>servletConfigInitParams的顺序从environment中找属性源
		// 存在的话返回其名字, 不存在的话返回systemProperties, evnrionment中有这个属性源的话, 就把JsonPropertySource类型的spring.application.json属性源添加到这个属性源的前面
		addJsonPropertySource(environment, new JsonPropertySource(propertyValue, flatten(map)));
	}
}

ConfigDataEnvironmentPostProcessor 解析配置文件 application.properties

classpath:/ 目录下找到了 application.properties 配置文件, 并最终封装为 OriginTrackedMapPropertySource, 其名称为 Config resource ‘class path resource [application.properties]’ via location ‘optional:classpath:/’

这里会根据 spring.profiles.active 和 spring.profiles.include 的配置加载指定的 properties 或 yml 配置文件

低版本使用的是 ConfigFileApplicationListener 来处理配置文件的, 这个ConfigDataEnvironmentPostProcessor 有点难看

类说明

PropertyResolver

在这里插入图片描述

  • PropertyResolver: 此接口用于在底层源之上解析一系列的属性值, 例如properties文件, yml文件等, 接口中定义了一系列读取, 解析, 判断是否包含指定属性的方法
  • ConfigurablePropertyResolver: 一个可配置的处理器. 不仅有父接口所有功能, 还扩展定义类型转换, 属性校验, 前缀后缀, 分隔符等一些列的功能
  • AbstractPropertyResolver: 它是对ConfigurablePropertyResolver的一个抽象实现,实现了了所有的接口方法,并且只提供一个抽象方法给子类去实现
    • PropertyPlaceholderHelper: 将字符串里的占位符内容,用我们配置的properties里的替换。这个是一个单纯的类,没有继承没有实现,而且简单无依赖. 在ServletContextPropertyUtils、SystemPropertyUtils、PropertyPlaceholderConfigurer里都有使用
  • PropertySourcesPropertyResolver: AbstractPropertyResolver封装了解析占位符的具体实现. PropertySourcesPropertyResolver作为它的子类主要是负责提供数据源

ConfigurableXXX是Spring的一种命名(设计)规范, 它表示可配置的XXX, 所以都会提供大量的set方法

Spring很多接口都是读写分离的,最顶层接口一般都只会提供只读方法,这是Spring框架设计的一般规律之一

PropertySource

在这里插入图片描述
PropertySources 和 PropertySource 的关系类似于 Map 和 Map.Entry 的关系, AbstractEnvironment 内部持有一个 PropertySources, 将来自各个源的配置文件 PropertySource 按照优先级顺序放在一个 List 中, 取属性值的时候优先有高优先级的 PropertySource 中查找

PropertySource

  • PropertySource: 表示name/source属性对的源的抽象基类, source可以是任何类似Map的类型. 类似于Map.Entry
  • EnumerablePropertySource: 可枚举的PropertySource, 提供了获取source中所有key的getPropertyNames方法
  • StubPropertySource: 用于在PropertySource的List中占位, 有些源可能不能过早初始化, 但是却必须得占一个靠前的位置. 重写了getProperty方法, 必定返回null
  • MapPropertySource: source的类型是Map<String, Object>, 用起来最方便
  • PropertiesPropertySourcr: 继承了MapPropertySource, 所以source的类型也是Map<String, Object>, Properties继承自HashTable<Object, Object>, 可以和Map强转
  • SystemEnvironmentPropertySource: 专门为系统环境变量(AbstractEnvironment#getSystemEnvironment())基于MapPropertySource做了一个定制. 支持 foo.bar/FOO.BAR/foo_bar/FOO_BAR 等都可以获得 foo.bar 的值. 供StandardEnvironment#customizePropertySources方法使用, 在添加 System Environment 到 PropertySources 中时, 直接初始化 SystemEnvironmentPropertySource 类型而不是 MapPropertySource

PropertySource重写了 equals 和 hashCode, name 相同则表示两个 PropertySource 相同

PropertySources

  • PropertySources: 持有多个 PropertySource 的容器, 类似于Map. 实现了 Iterable, 可以for遍历
  • MutablePropertySources: 可变的属性源, PropertySources 的实现类, 持有一个 List<PropertySource<?>> 类型的 CopyOnWriteArrayList, 提供了各种操作方法, 在AbstractEnvironment中持有MutablePropertySources实例, 即多个PropertySource

@PropertySource

@PropertySource注解提供了一种方便的声明性机制,用于将PropertySource添加到Spring的Environment中。与@Configuration注解结合使用, 在注册时可触发解析

  • 可以解析 < bean>标签 和 @Value注解 中的 占位符 ${…}
  • 可以解析 @PropertySource 注解中的资源路径中的 占位符 ${…}
@Configuration
@PropertySource("classpath:test.properties")

web相关配置的属性源的优先级是高于system相关的

通过@PropertySource导入的自定义配置, 后解析的配置中的key将覆盖先解析的配置中的同名key, 如果需要确保解析的顺序, 可以使用ConfigurableEnvironment和MutablePropertySources中的相关api

@PropertySource导入自定义配置,优先级最低(它会位于List最末尾)

Environment

在这里插入图片描述

  • Environment: 这个接口代表了当前应用正在运行的环境,为应用的两个重要方面建立抽象模型 profiles 和 properties 。关于 properties 访问的方法通过父接口PropertyResolver暴露出来了,本接口主要是扩展出访问 profiles 相关的入口
  • ConfigurableEnvironment: 扩展出了修改和配置profiles的一系列方法,包括用户自定义的和系统相关的属性。所有的环境实现类也都是它的实现. 它有两个分支, ConfigurableWebEnvironment, 供Web环境使用. AbstractEnvironment, 非Web环境使用
  • ConfigurableWebEnvironment: 提供 initPropertySources 方法 会设置web容器运行参数, 该方法是在刷新容器时的prepareRefresh调用
  • AbstractEnvironment: 环境变量的核心类, 完成了对active,default等相关方法的复写处理. customizePropertySources方法留给子类去填充, 属性源propertySources就是这个类的属性
  • StandardEnvironment, Spring应用在非web容器运行的环境, 初始化了系统环境变量, 系统参数
  • StandardServletEnvironment: 初始化Servlet上下文变量

通过实现接口 EnvironmentAware 或者直接 @Autowired 可以很方便的得到当前应用的环境:Environment

Spring容器最多只会实例化一个 Environment 示例对象

AbstractEnvironment

字段说明
// 支持同时激活多个profiles
// 这个属性缓存被激活配置文件的列表 仅仅是一个缓存的作用  spring.profiles.active指定的属性
private final Set<String> activeProfiles = new LinkedHashSet<>();
// 默认的也可以有多个
// 这个也是一个缓存作用保存当前默认的配置文件
private final Set<String> defaultProfiles = new LinkedHashSet<>(getReservedDefaultProfiles());
// 这个集合比较重要, 保存了所有SpringBoot上下文的配置信息, 包括系统信息/环境变量/Web容器初始化信息/...等等
private final MutablePropertySources propertySources;
// 属性解析器
// AbstractEnvironment实现了ConfigurableEnvironment, ConfigurableEnvironment实现了ConfigurablePropertyResolver
// 所以AbstractEnvironment里包含了ConfigurablePropertyResolver的所有操作, 而这些操作都由propertyResolver对象静态代理了
private final ConfigurablePropertyResolver propertyResolver;
  • profiles:配置。它代表应用在启动时注册到ApplicationContext中BeanDefinition的命名的逻辑分组
  • properties:属性。几乎在所有应用中都扮演着重要角色,他可能源自多种源头。例如属性文件,JVM系统属性,系统环境变量,JNDI,servlet上下文参数,Map等等,Environment对象和其相关的对象一起提供给用户一个方便用来配置和解析属性的服务
  • propertySources: 属性源, 实现类是MutablePropertySources, 持有一个CopyOnWriteArrayList存放List<PropertySource<?>>, 根据属性源的name判断是否是同一个属性源
  • propertyResolver: 属性解析器, 实现类是PropertySourcesPropertyResolver, 代理了AbstractEnvironment的所有PropertyResolver定义的操作
StandardServletEnvironment 对象构建
// AbstractEnvironment 的无参构造函数
public AbstractEnvironment() {
	this(new MutablePropertySources());
}
// AbstractEnvironment 的有参构造函数
protected AbstractEnvironment(MutablePropertySources propertySources) {
	this.propertySources = propertySources;
	this.propertyResolver = createPropertyResolver(propertySources);
	// 最后调用一下 customizePropertySources 方法将子类自定义的配置也添加到 propertySoueces 中
	customizePropertySources(propertySources);
}
// 初始化PropertySourcesPropertyResolver的时候, 已经把propertySources的引用传入其中了
protected ConfigurablePropertyResolver createPropertyResolver(MutablePropertySources propertySources) {
	return new PropertySourcesPropertyResolver(propertySources);
}
// 可由子类覆盖填充的自定义方法
protected void customizePropertySources(MutablePropertySources propertySources) {
}
// StandardServletEnvironment#customizePropertySources
protected void customizePropertySources(MutablePropertySources propertySources) {
	propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
	propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
	if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
		propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
	}
	super.customizePropertySources(propertySources);
}
// StandardEnvironment#customizePropertySources
protected void customizePropertySources(MutablePropertySources propertySources) {
	propertySources.addLast(new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
	propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}

通过无参构造函数初始化StandardServletEnvironment, 会调用父类的无参构造函数, 所以会调用StandardEnvironment的无参构造函数, 再调用AbstractEnvironment的无参构造函数, 这里会设置 propertySources 和 propertyResolver 两个属性, 然后调用子类即StadardServletEnvironment的customizePropertySources方法, 在传入的MutablePropertySources中添加 servletConfigInitParams 和 servletContextInitParams, 然后在调用其父类即StandardEnvironment的customizePropertySources方法, 在传入的MutablePropertySources中添加 systemProperties 和 systemEnvironment

这些 PropertySource 都是通过 addLast 方法添加进来的, 会先移除同名的 PropertySource, 然后再添加到列表尾部, 顺序如下

// PropertySource 顺序
servletConfigInitParams > servletContextInitParams > systemProperties > systemEnvironment

// 打印 StandardServletEnvironment
StandardServletEnvironment {activeProfiles=[], defaultProfiles=[default], propertySources=[StubPropertySource {name='servletConfigInitParams'}, StubPropertySource {name='servletContextInitParams'}, PropertiesPropertySource {name='systemProperties'}, SystemEnvironmentPropertySource {name='systemEnvironment'}]}

PropertySources 内部的 PropertySource 是按照优先级顺序添加进来的, 优先级高的排在最前面

配置获取
// AbstractEnvironment#getProperty(java.lang.String)
public String getProperty(String key) {
	return this.propertyResolver.getProperty(key);
}
// PropertySourcesPropertyResolver#getProperty(java.lang.String)
public String getProperty(String key) {
return getProperty(key, String.class, true);
}

// PropertySourcesPropertyResolver#getProperty(java.lang.String, java.lang.Class<T>, boolean)
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
	if (this.propertySources != null) {
		// 从前往后遍历每一个 PropertySource
		for (PropertySource<?> propertySource : this.propertySources) {
			if (logger.isTraceEnabled()) {
				logger.trace("Searching for key '" + key + "' in PropertySource '" +
						propertySource.getName() + "'");
			}
			Object value = propertySource.getProperty(key);
			if (value != null) {
				if (resolveNestedPlaceholders && value instanceof String) {
					value = resolveNestedPlaceholders((String) value);
				}
				logKeyFound(key, propertySource, value);
				return convertValueIfNecessary(value, targetValueType);
			}
		}
	}
	if (logger.isTraceEnabled()) {
		logger.trace("Could not find key '" + key + "' in any property source");
	}
	return null;
}

AbstractEnvironment 将获取配置的操作委托给 propertyResolver 了, 每次获取都会遍历 PropertySources 内的所有 PropertySource, 判断source内是否有对应的key, 如果没有则从下一个source内尝试取key, 都没有的话则返回null. 因为优先级高的 PropertySource 排在 List 的前面, 如果高优先级的 PropertySource 内有这个key, 则直接返回了, 就不会从低优先级的 PropertySource 里查找了

自定义配置的key, 尽量避免和系统配置的key重名, 否则可能因为优先级不同而无法正确获取

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值