SpringBoot 的配置解析是通过 Environment 来实现的。
Environment:
与属性相关的 Environment 对象的作用是为用户提供一个方便的服务接口,用于配置属性源并从中解析属性。
Environment 本身实现了 PropertyResolver 接口,最终会委托给 PropertySourcesPropertyResolver 去解析配置。
org.springframework.core.env.PropertySourcesPropertyResolver.getProperty
1 protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) { 2 if (this.propertySources != null) { 3 for (PropertySource<?> propertySource : this.propertySources) { // 1. 循环 PropertySources 4 if (logger.isTraceEnabled()) { 5 logger.trace("Searching for key '" + key + "' in PropertySource '" + 6 propertySource.getName() + "'"); 7 } 8 Object value = propertySource.getProperty(key); // 2. 从 PropertySource 中获取 key 对应的配置 9 if (value != null) { 10 if (resolveNestedPlaceholders && value instanceof String) { 11 value = resolveNestedPlaceholders((String) value); // 3. 解析占位符 ${} 12 } 13 logKeyFound(key, propertySource, value); 14 return convertValueIfNecessary(value, targetValueType); // 4. 转换成指定类型 15 } 16 } 17 } 18 if (logger.isTraceEnabled()) { 19 logger.trace("Could not find key '" + key + "' in any property source"); 20 } 21 return null; 22 }
最终获取配置就是去各种 PropertySource 中去取 key 对应的配置值,SpringBoot 支持多种类型的 PropertySource 扩展:
RandomValuePropertySource -- 对 random.int, random.long, random.uuid 的支持
MapPropertySource -- 对 K,V 形式的配置支持
JsonPropertySource -- 对 Json 类型的配置支持
SystemEnvironmentPropertySource -- 对配置 key 中使用 .、下划线、中划线、大小写的支持
CommandLinePropertySource -- 对启动命令参数的支持
ServletContextPropertySource -- 对 ServletContext 的 initParam 的支持
JndiPropertySource -- 对 JNDI 类型的配置支持
@PropertySource 注解的处理
ConfigurationClassParser#doProcessConfigurationClass()
在处理 @PropertySource 时,会加载注解中定义的资源文件,然后添加到 Environment#propertySources 中。
添加时,如果指定了多个资源文件,则后面定义的会放到 propertySources 的前面。这个会影响配置优先级,即后定义的优先级高
// 对所有的 @PropertySource 而言的 // 单个 @PropertySource 中定义多个资源文件地址也受这个规则影响 if (this.propertySourceNames.isEmpty()) { propertySources.addLast(propertySource); } else { String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1); propertySources.addBefore(firstProcessed, propertySource); }