【源码】Spring —— PropertySource 解读
前言
property 键值对属性的抽象封装类
注意跟 @PropertySource 注解区分,@PropertySource 跟
@Configuration 搭配使用可以添加属性集到 Environment中
PropertySource
此处仅贴出部分 属性 和 方法
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;
}
@SuppressWarnings("unchecked")
public PropertySource(String name) {
this(name, (T) new Object());
}
public String getName() {
return this.name;
}
public T getSource() {
return this.source;
}
public boolean containsProperty(String name) {
return (getProperty(name) != null);
}
@Nullable
public abstract Object getProperty(String name);
// 内部类,可以理解为占位用的临时 PropertySource
public static class StubPropertySource extends PropertySource<Object> {
public StubPropertySource(String name) {
super(name, new Object());
}
@Override
@Nullable
public String getProperty(String name) {
return null;
}
}
// 略
}
以 name 为维度,维护对应的 source,并提供对应的方法
EnumerablePropertySource
public abstract class EnumerablePropertySource<T> extends PropertySource<T> {
public EnumerablePropertySource(String name, T source) {
super(name, source);
}
protected EnumerablePropertySource(String name) {
super(name);
}
@Override
public boolean containsProperty(String name) {
return ObjectUtils.containsElement(getPropertyNames(), name);
}
public abstract String[] getPropertyNames();
}
类如其名,拓展了 getPropertyNames
方法提供对 PropertyNames 的枚举功能,同时重写了 containsProperty
的实现,其下的子类主要以 source 的类型为维度展开拓展
ServletContextPropertySource
public class ServletContextPropertySource extends EnumerablePropertySource<ServletContext> {
public ServletContextPropertySource(String name, ServletContext servletContext) {
super(name, servletContext);
}
@Override
public String[] getPropertyNames() {
return StringUtils.toStringArray(this.source.getInitParameterNames());
}
@Override
@Nullable
public String getProperty(String name) {
return this.source.getInitParameter(name);
}
}
source 为 ServletContext 的 PropertySource,getProperty
方法委托给了 ServletContext 的 getInitParameter
方法,实现了 getPropertyNames
方法
ServletConfigPropertySource
public class ServletConfigPropertySource extends EnumerablePropertySource<ServletConfig> {
public ServletConfigPropertySource(String name, ServletConfig servletConfig) {
super(name, servletConfig);
}
@Override
public String[] getPropertyNames() {
return StringUtils.toStringArray(this.source.getInitParameterNames());
}
@Override
@Nullable
public String getProperty(String name) {
return this.source.getInitParameter(name);
}
}
跟 ServletContextPropertySource 类似,source 为 ServletConfig
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());
}
}
source 为 Map<String, Object> 类型的 PropertySource ,最常用的 PropertySource ,注意 value 不能为 null,实现了 getPropertyNames
方法
SystemEnvironmentPropertySource
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);
}
// ...
}
针对 SystemEnvironment 会进行特殊匹配处理,会在匹配失败后对 name 进行一系列转换后再尝试匹配
PropertiesPropertySource
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();
}
}
}
source 为 Properties 的 PropertySource , Properties 本就是 Hashtable 的子类,相当于拓展了一种 MapPropertySource 的构造方法
ResourcePropertySource
public class ResourcePropertySource extends PropertiesPropertySource {
@Nullable
private final String resourceName;
public ResourcePropertySource(String name, EncodedResource resource) throws IOException {
super(name, PropertiesLoaderUtils.loadProperties(resource));
this.resourceName = getNameForResource(resource.getResource());
}
public ResourcePropertySource(EncodedResource resource) throws IOException {
super(getNameForResource(resource.getResource()), PropertiesLoaderUtils.loadProperties(resource));
this.resourceName = null;
}
// 略
}
在 PropertiesPropertySource 的基础上拓展了对 EncodedResource 的解析,相当于对 PropertiesPropertySource 构造的拓展
类图
总结
本文主要解读了 PropertySource 及其部分子类,可以看到它是对整个 属性键值对 的一种抽象,抽象出 getProperty
方法,让子类根据对不同类型 source 的实现提供对应的方法, 模板方法设计模式 的经典运用