概述
对于各种基于"名称/值"对(key/value pair
)的属性源,Spring
将其抽象成了抽象泛型类PropertySource<T>
。底层的属性源T
可以是容纳属性信息的任意类型,比如java.util.Properties
,java.util.Map
,ServletContext
,ServletConfig
对象,或者是命令行参数CommandLineArgs
对象。类PropertySource
的方法getSource()
用于获取底层的属性源对象T
。顶层的属性源对象经过PropertySource
封装,从而具有统一的访问方式。
以下是该抽象类的常用的一些具体实现类:
实现类 | 说明 |
---|---|
RandomValuePropertySource | 封装一个random 对象为属性源,用于获取int ,long ,uuid 随机数 |
MapPropertySource | 封装一个Map<String,Object> 对象为属性源 |
ServletConfigPropertySource | 封装一个ServletConfig 对象为属性源 |
ServletContextPropertySource | 封装一个ServletContext 对象为属性源 |
SystemEnvironmentPropertySource | 继承自MapPropertySource ,被 StandardEnvironment 用于将System.getenv() 封装成一个属性源在获取属性的属性名上针对不同环境做了处理,比如 getProperty("foo.bar") 会匹配"foo.bar",“foo_bar”,“FOO.BAR"或者"FOO_BAR”,如果想获取的属性名称中含有- ,会被当作_ 处理。 |
SimpleCommandLinePropertySource | 将命令行参数字符串数组转换成一个CommandLineArgs 对象,然后封装成一个属性源比如一个 springboot 应用SpringApplication 启动时,如果提供了命令行参数,他们就会被封装成一个SimpleCommandLinePropertySource 对象放到上下文环境中去。 |
PropertiesPropertySource | 继承自MapPropertySource ,将一个java.util.Properties 对象封装为属性源 |
它们之间的继承关系如下:
注意这里的
EnumerablePropertySource
抽象类,它为PropertySource
增加了一个String[] getPropertyNames()
方法,要求实现类提供,从而使相应的属性源对象可以被列举访问所包含的各个属性值对。
源码分析
// 仅分析核心抽象
package org.springframework.core.env;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Abstract base class representing a source of name/value property pairs. The underlying
* #getSource() source object may be of any type T that encapsulates
* properties. Examples include java.util.Properties objects, java.util.Map
* objects, ServletContext and ServletConfig objects (for access to init
* parameters). Explore the PropertySource type hierarchy to see provided
* implementations.
*
* 抽象基类,用于表示一个名称/值属性对的来源,简称为属性源。底层的属性源对象的类型通过泛型类T指定。
*
* PropertySource objects are not typically used in isolation, but rather
* through a PropertySources object, which aggregates property sources and in
* conjunction with a PropertyResolver implementation that can perform
* precedence-based searches across the set of PropertySources.
*
* PropertySource 对象通常并不孤立使用,而是将多个PropertySource对象封装成一个PropertySources
* 对象来使用。另外还会有一个PropertyResolver属性解析器工作在PropertySources对象上,基于特定的优先级,
* 来访问这些属性源对象中的属性。
*
* PropertySource identity is determined not based on the content of
* encapsulated properties, but rather based on the #getName() name of the
* PropertySource alone. This is useful for manipulating PropertySource
* objects when in collection contexts. See operations in MutablePropertySources
* as well as the #named(String) and #toString() methods for details.
* PropertySource有一个唯一标识id,这个唯一标识id不是基于所封装的属性内容,而是基于指定给
* 这个PropertySource对象的名称属性#getName()。
*
* Note that when working with
* org.springframework.context.annotation.Configuration Configuration classes that
* the @org.springframework.context.annotation.PropertySource PropertySource
* annotation provides a convenient and declarative way of adding property sources to the
* enclosing Environment.
*
* @author Chris Beams
* @since 3.1
* @param <T> the source type
* @see PropertySources
* @see PropertyResolver
* @see PropertySourcesPropertyResolver
* @see MutablePropertySources
* @see org.springframework.context.annotation.PropertySource
*/
public abstract class PropertySource<T> {
protected final Log logger = LogFactory.getLog(getClass());
protected final String name;
protected final T source;
/**
* Create a new PropertySource with the given name and source object.
* 将某个T类型的底层属性源source对象封装成一个特定名称为name的PropertySource对象
*/
public PropertySource(String name, T source) {
Assert.hasText(name, "Property source name must contain at least one character");
Assert.notNull(source, "Property source must not be null");
this.name = name;
this.source = source;
}
/**
* Create a new PropertySource with the given name and with a new
* Object instance as the underlying source.
* Often useful in testing scenarios when creating anonymous implementations
* that never query an actual source but rather return hard-coded values.
* 使用一个空对象构建一个指定名称为name的PropertySource对象。通常用于测试目的。
*/
@SuppressWarnings("unchecked")
public PropertySource(String name) {
this(name, (T) new Object());
}
/**
* Return the name of this PropertySource.获取属性源名称
*/
public String getName() {
return this.name;
}
/**
* Return the underlying source object for this PropertySource.
* 获取所封装的底层属性源对象,类型为泛型T。
*/
public T getSource() {
return this.source;
}
/**
* Return whether this PropertySource contains the given name.
* This implementation simply checks for a null return value
* from #getProperty(String). Subclasses may wish to implement
* a more efficient algorithm if possible.
* 查看指定名称为name的属性是否被当前PropertySource对象包含,
* 判断方法:尝试获取该名称的属性值,如果值不为null认为是包含;否则认为是不包含。
* 继承类可以提供不同的判断方法和实现。
* @param name the property name to find
*/
public boolean containsProperty(String name) {
return (getProperty(name) != null);
}
/**
* Return the value associated with the given name,
* or null if not found.
* 获取指定名称为name的属性的值,如果该属性不存在于该PropertySource对象,返回null
* @param name the property to find
* @see PropertyResolver#getRequiredProperty(String)
*/
@Nullable
public abstract Object getProperty(String name);
}