类继承关系
代码分析
PropertiesLoaderSupport类是一个抽象类,定义了一些基础性的操作,可以从声明的方法上看出来:
往下走,就看出东西来了。PropertyResourceConfigurer类是非常重要的一环,类声明如下:
public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport
implements BeanFactoryPostProcessor, PriorityOrdered {
}
可以看到PropertyResourceConfigurer类实现了BeanFactoryPostProcessor接口,这个接口干嘛用的呢?
Spring IoC容器允许BeanFactoryPostProcessor在容器实例化任何bean之前读取bean的定义(配置元数据),并可以修改它。同时可以定义多个BeanFactoryPostProcessor,通过设置’order’属性来确定各个BeanFactoryPostProcessor执行顺序。
关键就是那句“读取bean的定义(配置元数据),并可以修改它”。我们看看这个接口的源码:
@FunctionalInterface
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
再来看看PropertyResourceConfigurer类对BeanFactoryPostProcessor 接口的实现:
/**
* {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and
* {@linkplain #processProperties process} properties against the given bean factory.
* @throws BeanInitializationException if any properties cannot be loaded
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
Properties mergedProps = mergeProperties(); //合并属性
// Convert the merged properties, if necessary.
convertProperties(mergedProps); //转换属性
// Let the subclass process the properties.
processProperties(beanFactory, mergedProps); //将属性赋值给bean对象
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
其中,mergeProperties()方法是PropertiesLoaderSupport类提供的,代码如下:
/**
* localOverride开关用于控制优先使用代码属性还是文件中属性
* localOverride开关开启时优先使用文件中属性;关闭时优先使用代码中属性,默认关闭
*/
protected Properties mergeProperties() throws IOException {
Properties result = new Properties();
//如果localOverride开关开启,那么载入文件中的属性(优先使用代码中属性)
if (this.localOverride) {
// Load properties from file upfront, to let local properties override.
loadProperties(result);
}
//如果localProperties不为空,那么将localProperties属性覆盖到文件中的属性
if (this.localProperties != null) {
for (Properties localProp : this.localProperties) {
CollectionUtils.mergePropertiesIntoMap(localProp, result);
}
}
//如果localOverride开关关闭,那么载入文件中的属性(优先使用文件中属性)
if (!this.localOverride) {
// Load properties from file afterwards, to let those properties override.
loadProperties(result);
}
return result;
}
/**
* Load properties into the given instance.
* @param props the Properties instance to load into
* @throws IOException in case of I/O errors
* @see #setLocations
*/
protected void loadProperties(Properties props) throws IOException {
if (this.locations != null) {
for (Resource location : this.locations) {
if (logger.isDebugEnabled()) {
logger.debug("Loading properties file from " + location);
}
try {
PropertiesLoaderUtils.fillProperties(
props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister);
}
catch (FileNotFoundException | UnknownHostException ex) {
if (this.ignoreResourceNotFound) {
if (logger.isInfoEnabled()) {
logger.info("Properties resource not found: " + ex.getMessage());
}
}
else {
throw ex;
}
}
}
}
}
convertProperties(mergedProps)方法是PropertyResourceConfigurer类提供的,代码如下:
/**
* Convert the given merged properties, converting property values
* if necessary. The result will then be processed.
* <p>The default implementation will invoke {@link #convertPropertyValue}
* for each property value, replacing the original with the converted value.
* @param props the Properties to convert
* @see #processProperties
*/
protected void convertProperties(Properties props) {
Enumeration<?> propertyNames = props.propertyNames();
while (propertyNames.hasMoreElements()) {
String propertyName = (String) propertyNames.nextElement();
String propertyValue = props.getProperty(propertyName);
String convertedValue = convertProperty(propertyName, propertyValue);
if (!ObjectUtils.nullSafeEquals(propertyValue, convertedValue)) {
props.setProperty(propertyName, convertedValue);
}
}
}
processProperties(beanFactory, mergedProps)是个抽象方法,由子类实现。顾名思义,是把属性赋值给bean对象用的,代码如下:
/**
* Apply the given Properties to the given BeanFactory.
* @param beanFactory the BeanFactory used by the application context
* @param props the Properties to apply
* @throws org.springframework.beans.BeansException in case of errors
*/
protected abstract void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
throws BeansException;
PropertyPlaceholderConfigurer类对processProperties()方法的实现如下:
/**
* Visit each bean definition in the given bean factory and attempt to replace ${...} property
* placeholders with values from the given properties.
*/
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
doProcessProperties(beanFactoryToProcess, valueResolver);
}
PropertyPlaceholderConfigurer类中processProperties()方法是个嫁接方法,真正的处理逻辑它丢给了doProcessProperties()方法,代码如下:
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
//将属性包装给BeanDefinitionVisitor,由BeanDefinitionVisitor完成后续属性设置
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
//取出所有Bean的名称
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
//逐个遍历所有bean
for (String curName : beanNames) {
// Check that we're not parsing our own bean definition,
// to avoid failing on unresolvable placeholders in properties file locations.
if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
try {
//BeanDefinitionVisitor完成对属性的设置
visitor.visitBeanDefinition(bd);
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
}
}
}
// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
beanFactoryToProcess.resolveAliases(valueResolver);
// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}
BeanDefinitionVisitor 类的visitor()方法代码:
/**
* Traverse the given BeanDefinition object and the MutablePropertyValues
* and ConstructorArgumentValues contained in them.
* @param beanDefinition the BeanDefinition object to traverse
* @see #resolveStringValue(String)
*/
public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
if (beanDefinition.hasPropertyValues()) {
visitPropertyValues(beanDefinition.getPropertyValues());
}
if (beanDefinition.hasConstructorArgumentValues()) {
ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
visitIndexedArgumentValues(cas.getIndexedArgumentValues());
visitGenericArgumentValues(cas.getGenericArgumentValues());
}
}
其中,visitPropertyValues()方法的实现:
protected void visitPropertyValues(MutablePropertyValues pvs) {
PropertyValue[] pvArray = pvs.getPropertyValues();
for (PropertyValue pv : pvArray) {
Object newVal = resolveValue(pv.getValue());
if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
pvs.add(pv.getName(), newVal);
}
}
}
resolveValue()方法 的实现:
@SuppressWarnings("rawtypes")
@Nullable
protected Object resolveValue(@Nullable Object value) {
if (value instanceof BeanDefinition) {
visitBeanDefinition((BeanDefinition) value);
}
else if (value instanceof BeanDefinitionHolder) {
visitBeanDefinition(((BeanDefinitionHolder) value).getBeanDefinition());
}
else if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
String newBeanName = resolveStringValue(ref.getBeanName());
if (newBeanName == null) {
return null;
}
if (!newBeanName.equals(ref.getBeanName())) {
return new RuntimeBeanReference(newBeanName);
}
}
else if (value instanceof RuntimeBeanNameReference) {
RuntimeBeanNameReference ref = (RuntimeBeanNameReference) value;
String newBeanName = resolveStringValue(ref.getBeanName());
if (newBeanName == null) {
return null;
}
if (!newBeanName.equals(ref.getBeanName())) {
return new RuntimeBeanNameReference(newBeanName);
}
}
else if (value instanceof Object[]) {
visitArray((Object[]) value);
}
else if (value instanceof List) {
visitList((List) value);
}
else if (value instanceof Set) {
visitSet((Set) value);
}
else if (value instanceof Map) {
visitMap((Map) value);
}
else if (value instanceof TypedStringValue) {
TypedStringValue typedStringValue = (TypedStringValue) value;
String stringValue = typedStringValue.getValue();
if (stringValue != null) {
String visitedString = resolveStringValue(stringValue);
typedStringValue.setValue(visitedString);
}
}
else if (value instanceof String) {
return resolveStringValue((String) value);
}
return value;
}
protected void visitArray(Object[] arrayVal) {
for (int i = 0; i < arrayVal.length; i++) {
Object elem = arrayVal[i];
Object newVal = resolveValue(elem);
if (!ObjectUtils.nullSafeEquals(newVal, elem)) {
arrayVal[i] = newVal;
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void visitList(List listVal) {
for (int i = 0; i < listVal.size(); i++) {
Object elem = listVal.get(i);
Object newVal = resolveValue(elem);
if (!ObjectUtils.nullSafeEquals(newVal, elem)) {
listVal.set(i, newVal);
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void visitSet(Set setVal) {
Set newContent = new LinkedHashSet();
boolean entriesModified = false;
for (Object elem : setVal) {
int elemHash = (elem != null ? elem.hashCode() : 0);
Object newVal = resolveValue(elem);
int newValHash = (newVal != null ? newVal.hashCode() : 0);
newContent.add(newVal);
entriesModified = entriesModified || (newVal != elem || newValHash != elemHash);
}
if (entriesModified) {
setVal.clear();
setVal.addAll(newContent);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void visitMap(Map<?, ?> mapVal) {
Map newContent = new LinkedHashMap();
boolean entriesModified = false;
for (Map.Entry entry : mapVal.entrySet()) {
Object key = entry.getKey();
int keyHash = (key != null ? key.hashCode() : 0);
Object newKey = resolveValue(key);
int newKeyHash = (newKey != null ? newKey.hashCode() : 0);
Object val = entry.getValue();
Object newVal = resolveValue(val);
newContent.put(newKey, newVal);
entriesModified = entriesModified || (newVal != val || newKey != key || newKeyHash != keyHash);
}
if (entriesModified) {
mapVal.clear();
mapVal.putAll(newContent);
}
}
/**
* Resolve the given String value, for example parsing placeholders.
* @param strVal the original String value
* @return the resolved String value
*/
@Nullable
protected String resolveStringValue(String strVal) {
if (this.valueResolver == null) {
throw new IllegalStateException("No StringValueResolver specified - pass a resolver " +
"object into the constructor or override the 'resolveStringValue' method");
}
String resolvedValue = this.valueResolver.resolveStringValue(strVal);
// Return original String if not modified.
return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
}
到这里,属性替换就完成啦。