总所周知,Spring IOC 是通过调用 AbstractApplicationContext.refresh() 方法进行初始化的,但是 Spring 在调用 refresh() 方法之前,先会调用 AbstractRefreshableConfigApplicationContext.setConfigLocations() 方法来处理配置文件数组,解析后的配置文件地址又需要写入 Spring 的 Environment,因此 Spring会在这一步通过 AbstractApplicationContext.createEnvironment() 方法对 Environment 进行实例化。
public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
implements BeanNameAware, InitializingBean {
...
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
}
先看 getEnvironment() 方法:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
...
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
}
在 AbstractApplicationContext.createEnvironment() 方法中,直接返回了一个 StandardEnvironment 实例。关于 StandardEnvironment 的类图如下:
接着看 StandardEnvironment 类,发现 StandardEnvironment 类中没有定义构造方法,因此顺势查看其父类 AbstractEnvironment 的构造器。其父类 AbstractEnvironment 的构造方法正好调用 StandardEnvironment 中重写了的 customizePropertySources() 方法。
public class StandardEnvironment extends AbstractEnvironment {
/** System environment property source name: {@value} */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value} */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
@Override
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()));
}
}
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
...
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
if (logger.isDebugEnabled()) {
logger.debug("Initialized " + getClass().getSimpleName() + " with PropertySources " + this.propertySources);
}
}
...
protected void customizePropertySources(MutablePropertySources propertySources) {
}
}
MutablePropertySources 继承线
接着看 StandardEnvironment 的组合类 MutablePropertySources。在前面的类图中我们看到,MutablePropertySources 实现了 PropertySources 接口,而 PropertySources 实际上是继承了 Iterable<PropertySource<?>>,因此 PropertySources 本质上就是一个 PropertySource 类的遍历器,从这里其实可以猜测 PropertySource 可能是某种自定义的数据结构。接着查看 PropertySource 类,发现 PropertySource 代表的是一种键值对模式的数据源。局部变量中包含有一个表示数据源名字的 name 和一个表示具体数据源泛型 T 的 source,并定义了获取局部变量的方法。代码如下所示:
public interface PropertySources extends Iterable<PropertySource<?>> {
/**
* Return whether a property source with the given name is contained.
* @param name the {@linkplain PropertySource#getName() name of the property source} to find
*/
boolean contains(String name);
/**
* Return the property source with the given name, {@code null} if not found.
* @param name the {@linkplain PropertySource#getName() name of the property source} to find
*/
@Nullable
PropertySource<?> get(String name);
}
接着看 MutablePropertySources 类,MutablePropertySources 实现了 PropertySources 接口,内部维护了一个 CopyOnWriteArrayList 对象,用以存储多个数据源,并将自身的方法也封装为一个 List。
PropertiesPropertySource 继承线
到这里,再回过头去看 StandardEnvironment.customizePropertySources() 方法关联的两个类 PropertiesPropertySource 和 SystemEnvironmentPropertySource,在类图中可以看出,PropertiesPropertySource 和 SystemEnvironmentPropertySource 均继承自 MapPropertySource,而 MapPropertySource 继承自抽象类 EnumerablePropertySource<Map<String, Object>> ,EnumerablePropertySource 则又继承了 PropertySource。到这里,通过类名再结合上一节的分析,可以猜测 MapPropertySource 的操作应该和 Map 相同。先看抽象类 EnumerablePropertySource:
public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
public EnumerablePropertySource(String name, T source) {
super(name, source);
}
protected EnumerablePropertySource(String name) {
super(name);
}
/**
* Return whether this {@code PropertySource} contains a property with the given name.
* <p>This implementation checks for the presence of the given name within the
* {@link #getPropertyNames()} array.
* @param name the name of the property to find
*/
@Override
public boolean containsProperty(String name) {
return ObjectUtils.containsElement(getPropertyNames(), name);
}
/**
* Return the names of all properties contained by the
* {@linkplain #getSource() source} object (never {@code null}).
*/
public abstract String[] getPropertyNames();
}
EnumerablePropertySource 中添加了抽象方法 getPropertyNames() ,要求子类返回内存持有的键名列表,同时实现了containsProperty方法,通过判断所给的键名是否存在上述返回的键名列表中从而判断是否包含该键名。再继续看 MapPropertySource 类:
public class MapPropertySource extends EnumerablePropertySource<Map<String, Object>> {
public MapPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
@Override
@Nullable
public Object getProperty(String name) {
return this.source.get(name);
}
@Override
public boolean containsProperty(String name) {
return this.source.containsKey(name);
}
@Override
public String[] getPropertyNames() {
return StringUtils.toStringArray(this.source.keySet());
}
}
可以看出,MapPropertySource 内部维护了一个 Map 用来保存键值内容。并将自身的方法封装为一个 Map。最后来看 PropertiesPropertySource 和 SystemEnvironmentPropertySource 类,这两个类均为 MapPropertySource 的包装类。
public class PropertiesPropertySource extends MapPropertySource {
@SuppressWarnings({"rawtypes", "unchecked"})
public PropertiesPropertySource(String name, Properties source) {
super(name, (Map) source);
}
protected PropertiesPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
@Override
public String[] getPropertyNames() {
synchronized (this.source) {
return super.getPropertyNames();
}
}
}
public class SystemEnvironmentPropertySource extends MapPropertySource {
public SystemEnvironmentPropertySource(String name, Map<String, Object> source) {
super(name, source);
}
@Override
public boolean containsProperty(String name) {
return (getProperty(name) != null);
}
@Override
@Nullable
public Object getProperty(String name) {
String actualName = resolvePropertyName(name);
if (logger.isDebugEnabled() && !name.equals(actualName)) {
logger.debug("PropertySource '" + getName() + "' does not contain property '" + name +
"', but found equivalent '" + actualName + "'");
}
return super.getProperty(actualName);
}
protected final String resolvePropertyName(String name) {
Assert.notNull(name, "Property name must not be null");
String resolvedName = checkPropertyName(name);
if (resolvedName != null) {
return resolvedName;
}
String uppercasedName = name.toUpperCase();
if (!name.equals(uppercasedName)) {
resolvedName = checkPropertyName(uppercasedName);
if (resolvedName != null) {
return resolvedName;
}
}
return name;
}
@Nullable
private String checkPropertyName(String name) {
// Check name as-is
if (containsKey(name)) {
return name;
}
// Check name with just dots replaced
String noDotName = name.replace('.', '_');
if (!name.equals(noDotName) && containsKey(noDotName)) {
return noDotName;
}
// Check name with just hyphens replaced
String noHyphenName = name.replace('-', '_');
if (!name.equals(noHyphenName) && containsKey(noHyphenName)) {
return noHyphenName;
}
// Check name with dots and hyphens replaced
String noDotNoHyphenName = noDotName.replace('-', '_');
if (!noDotName.equals(noDotNoHyphenName) && containsKey(noDotNoHyphenName)) {
return noDotNoHyphenName;
}
// Give up
return null;
}
private boolean containsKey(String name) {
return (isSecurityManagerPresent() ? this.source.keySet().contains(name) : this.source.containsKey(name));
}
protected boolean isSecurityManagerPresent() {
return (System.getSecurityManager() != null);
}
}
最后再回到 StandardEnvironment.customizePropertySources() 方法。其中,系统相关属性SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME 的数据源来源于 System.getProperties(),而应用环境变量相关属性SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME 的数据来源于System.getenv()。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
继续回到 AbstractRefreshableConfigApplicationContext.setConfigLocations() 方法
public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
implements BeanNameAware, InitializingBean {
...
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
}
接下来看一下 resolveRequiredPlaceholders(path) 方法,该方法在是 PropertyResolver 接口申明的。StandardEnvironment 类继承自抽象类 AbstractEnvironment,AbstractEnvironment 实现了 ConfigurableEnvironment 接口,ConfigurableEnvironment 继承自 Environment 和 ConfigurablePropertyResolver 接口,Environment 和 ConfigurablePropertyResolver 则继承自顶级接口 PropertyResolver。
PropertyResolver 定义了一系列接口,以提供了对外根据键名获取相应值的功能,同时提供了类型转换和占位符替换的功能。其中 getProperty() 方法中如果 key 不存在返回空,而 getRequiredProperty() 方法中如果 key 不存在则直接报错。
而 resolveRequiredPlaceholders(path) 方法由 AbstractEnvironment 实现。
public abstract class AbstractEnvironment implements ConfigurableEnvironment {
protected final Log logger = LogFactory.getLog(getClass());
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
private final ConfigurablePropertyResolver propertyResolver =
new PropertySourcesPropertyResolver(this.propertySources);
...
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
}
PropertySourcesPropertyResolver 继承线
AbstractEnvironment 内部持有一个 PropertySourcesPropertyResolver 类,来看一下 PropertySourcesPropertyResolver 的继承结构。PropertySourcesPropertyResolver 类继承自 AbstractPropertyResolver 抽象类,AbstractPropertyResolver 类继承自 ConfigurablePropertyResolver 接口,ConfigurablePropertyResolver 接口继承自 PropertyResolver 接口,接下来先看一下 PropertyResolver 接口。
/**
* Interface for resolving properties against any underlying source.
*/
public interface PropertyResolver {
boolean containsProperty(String key);
@Nullable
String getProperty(String key);
String getProperty(String key, String defaultValue);
@Nullable
<T> T getProperty(String key, Class<T> targetType);
<T> T getProperty(String key, Class<T> targetType, T defaultValue);
String getRequiredProperty(String key) throws IllegalStateException;
<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;
String resolvePlaceholders(String text);
String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;
}
在注解中有些到,该接口主要负责所有基类 source 解析为 priperty 的工作。提供了对外根据 property key 获取相应 property value 的功能,不存在则返回 null,其中 getRequiredProperty() 则是严格匹配,不存在则会报错。接着看其子接口 ConfigurablePropertyResolver 。
/**
* Configuration interface to be implemented by most if not all {@link PropertyResolver}
* types. Provides facilities for accessing and customizing the
* {@link org.springframework.core.convert.ConversionService ConversionService}
* used when converting property values from one type to another.
*/
public interface ConfigurablePropertyResolver extends PropertyResolver {
/**
* Return the {@link ConfigurableConversionService} used when performing type
* conversions on properties.
*/
ConfigurableConversionService getConversionService();
/**
* Set the {@link ConfigurableConversionService} to be used when performing type
* conversions on properties.
*/
void setConversionService(ConfigurableConversionService conversionService);
/**
* Set the prefix that placeholders replaced by this resolver must begin with.
*/
void setPlaceholderPrefix(String placeholderPrefix);
/**
* Set the suffix that placeholders replaced by this resolver must end with.
*/
void setPlaceholderSuffix(String placeholderSuffix);
/**
* Specify the separating character between the placeholders replaced by this
* resolver and their associated default value, or {@code null} if no such
* special character should be processed as a value separator.
*/
void setValueSeparator(@Nullable String valueSeparator);
/**
* Set whether to throw an exception when encountering an unresolvable placeholder
* nested within the value of a given property. A {@code false} value indicates strict
* resolution, i.e. that an exception will be thrown. A {@code true} value indicates
* that unresolvable nested placeholders should be passed through in their unresolved
* ${...} form.
*/
void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);
/**
* Specify which properties must be present, to be verified by
* {@link #validateRequiredProperties()}.
*/
void setRequiredProperties(String... requiredProperties);
/**
* Validate that each of the properties specified by
* {@link #setRequiredProperties} is present and resolves to a
* non-{@code null} value.
*/
void validateRequiredProperties() throws MissingRequiredPropertiesException;
}
在该接口的注释中可以看到 “when converting property values from one type to another.” 意思很清楚了,负责属性值的转换工作。在继续看 AbstractPropertyResolver 类。
AbstractPropertyResolver 类基本上提供了除 PropertySource 外的实现。而重要的 getProperty() 方法则在 AbstractPropertyResolver 的子类 PropertySourcesPropertyResolver 实现。
public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
@Nullable
private final PropertySources propertySources;
@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
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.isDebugEnabled()) {
logger.debug("Could not find key '" + key + "' in any property source");
}
return null;
}
}
StandardEnvironment 继承线
最后,再来看一下最终的 StandardEnvironment 继承线。StandardEnvironment 类继承自抽象类 AbstractEnvironment,AbstractEnvironment 实现了 ConfigurableEnvironment 接口,ConfigurableEnvironment 继承自 Environment 和 ConfigurablePropertyResolver 接口,Environment 和 ConfigurablePropertyResolver 则继承自顶级接口 PropertyResolver。
PropertyResolver 类在上一节中有分析过,这里就不赘述了,继续看 Environment 类。
public interface Environment extends PropertyResolver {
String[] getActiveProfiles();
String[] getDefaultProfiles();
boolean acceptsProfiles(String... profiles);
}
Environment 继承自 PropertyResolver 接口,增加了 Profiles 功能,即我们平时看到的,多环境特性,能够在不同环境下加载不同的配置,可以查询给定的 profiles 是否在 active 状态以及获得所有的 active profiles。ConfigurablePropertyResolver 前面也提到过,也不说了,往下看 ConfigurableEnvironment 接口。
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver {
void setActiveProfiles(String... profiles);
void addActiveProfile(String profile);
void setDefaultProfiles(String... profiles);
MutablePropertySources getPropertySources();
Map<String, Object> getSystemProperties();
Map<String, Object> getSystemEnvironment();
void merge(ConfigurableEnvironment parent);
}
ConfigurableEnvironment 继承自 Environment,老规矩,又是添加了修改的扩展接口,同时增加了获取系统参数的接口。另外,该接口也继承自 ConfigurablePropertyResolver,有了键值对数据源管理、获取和处理的能力,集合 Environment 接口的功能,能够达到在不同环境下通过加载不同配置源实现环境隔离的效果。
AbstractEnvironment
AbstractEnvironment 是 ConfigurablePropertyResolver 的实现,提供了默认的环境源default,同时内部组合使用PropertySourcesPropertyResolver 作为 PropertyResolver 的实现。实现了 Environment 的大部分功能。
它还维护了一个 MutablePropertySources 对象,用于存储多个数据源,在 Context 的父子上下文中,通过 merge 方法,能够将父上文中的环境变量内容添加进来(在AbstractApplicationContext设置父Context时,会将父 Environment 进行合并)。同时还有一个方法 customizePropertySources,会在构造方法中进行调用,开放给子类添加默认的键值对源,如下:
StandardEnvironment
最后是 StandardEnvironment 类,继承自 AbstractEnvironment,重写了 customizePropertySources 方法,在该方法中添加了系统相关的属性和应用环境变量相关的属性的键值对源。而这两个数据源来自于前面提到的PropertySource实现。其中,系统相关属性SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME
的数据源来源于System.getProperties()
,而应用环境变量相关属性SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
则来源于 System.getenv()
。
参考来源: