简介
PropertySource是一个抽象类,用于存放key-value键值对的抽象,value可以使任意类型,如:ServletContext、ServletConfig、Properties等。类结构简化如下:
public abstract class PropertySource<T> {
//属性名称(不可更改)
protected final String name;
//属性值(不可更改)
protected final T source;
//构造函数
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;
}
重要的类继承关系如下:
EnumerablePropertySource
是PropertySource最重要的一个分支,大部分的实现类都继承于它, Enumerable表示可枚举的意思,新增抽象方法getPropertyNames()表示每个key都应该是可以枚举的,同时重写containsProperty()方法通过getPropertyNames()方法返回的key进行判断。
public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
/**
* 重写通过调用getPropertyNames()方法返回的所有key中是否包含给定的name
*/
@Override
public boolean containsProperty(String name) {
return ObjectUtils.containsElement(getPropertyNames(), name);
}
/**
* 返回所有的key,如MapPropertySource实现类返回source类型为Map所有key
*/
public abstract String[] getPropertyNames();
MapPropertySource
这也是一个相对重要的实现类,其source类型为Map,实现了getProperty()方法,重写了containsProperty()、getPropertyNames()方法
public class MapPropertySource extends EnumerablePropertySource<Map<String, Object>> {
/*
* 从类型为Map<String, Object>的source获取值
*/
@Override
@Nullable
public Object getProperty(String name) {
return this.source.get(name);
}
/**
* 判断类型为Map的source是否包含某个key
*/
@Override
public boolean containsProperty(String name) {
return this.source.containsKey(name);
}
/**
* 从类型为Map的source取出所有key
*/
@Override
public String[] getPropertyNames() {
return StringUtils.toStringArray(this.source.keySet());
}
}
SystemEnvironmentPropertySource
继承自MapPropertySource,它的source也是一个map,但来源于系统环境。与MapPropertySource不同的是,取值时它将会忽略大小写,”.”和”_”将会转化,因此在获取value之前,都会对name进行一次处理。
public class SystemEnvironmentPropertySource extends MapPropertySource {
/**
* 根据属性名获取属性值
*/
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) {
// 如果已经存在直接返回
if (containsKey(name)) {
return name;
}
// "."与"_"等价
String noDotName = name.replace('.', '_');
if (!name.equals(noDotName) && containsKey(noDotName)) {
return noDotName;
}
// / "-"与"_"等价
String noHyphenName = name.replace('-', '_');
if (!name.equals(noHyphenName) && containsKey(noHyphenName)) {
return noHyphenName;
}
// 继续寻找替换 "-"与"_"等价
String noDotNoHyphenName = noDotName.replace('-', '_');
if (!noDotName.equals(noDotNoHyphenName) && containsKey(noDotNoHyphenName)) {
return noDotNoHyphenName;
}
return null;
}
}
PropertiesPropertySource
跟MapPropertySource
没太大区别,唯一的操作就是对source进行了加锁,从而避免并发场景下的线程不安全因素。
public class PropertiesPropertySource extends MapPropertySource {
/**
* 加锁,并发控制
*/
@Override
public String[] getPropertyNames() {
synchronized (this.source) {
return super.getPropertyNames();
}
}
}
ResourcePropertySource
继承至PropertiesPropertySource,source来自于一个Resource资源,构造函数比较丰富,便于开发者使用
public class ResourcePropertySource extends PropertiesPropertySource {
/**
* 构造函数入参resource为资源,通过PropertiesLoaderUtils加载资源成Properties(继承至Hashtable)
*/
public ResourcePropertySource(String name, Resource resource) throws IOException {
super(name, PropertiesLoaderUtils.loadProperties(new EncodedResource(resource)));
this.resourceName = getNameForResource(resource);
}
}
ServletContextPropertySource
source 为 ServletContext,getProperty
方法委托给了ServletContext 的 getInitParameter
方法用于访问Servlet上下文信息InitParameters
public class ServletContextPropertySource extends EnumerablePropertySource<ServletContext> {
/**
* ServletContext InitParameterName的key
*/
@Override
public String[] getPropertyNames() {
return StringUtils.toStringArray(this.source.getInitParameterNames());
}
/**
* ServletContext通过name获取属性值
*/
@Override
@Nullable
public String getProperty(String name) {
return this.source.getInitParameter(name);
}
}
MutablePropertySources
它是PropertySource的一个复数形式,它如同一个容器可以包含一个或者多个PropertySource。并且提供对他们操作的方法,它更像是一个管理器,管理着所有的PropertySource,如:提供增、删、改、查方法。
public class MutablePropertySources implements PropertySources {
/**
* 新增PropertySource
*/
public void addFirst(PropertySource<?> propertySource) {
synchronized (this.propertySourceList) {
removeIfPresent(propertySource);
this.propertySourceList.add(0, propertySource);
}
}
}
/**
* 删除
*/
public PropertySource<?> remove(String name) {
synchronized (this.propertySourceList) {
int index = this.propertySourceList.indexOf(PropertySource.named(name));
return (index != -1 ? this.propertySourceList.remove(index) : null);
}
}
/**
* 修改
*/
public void replace(String name, PropertySource<?> propertySource) {
synchronized (this.propertySourceList) {
int index = assertPresentAndGetIndex(name);
this.propertySourceList.set(index, propertySource);
}
}
/**
* 查找
*/
public PropertySource<?> get(String name) {
for (PropertySource<?> propertySource : this.propertySourceList) {
if (propertySource.getName().equals(name)) {
return propertySource;
}
}
return null;
}