類繼承關係
代碼分析
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);
}
到這裏,屬性替換就完成啦。