Spring 加载 *.properties 文件的源码分析

Spring 是如何加载 *.properties 文件的?

1、属性加载器

PropertyPlaceholderConfigurer.java 类图结构
《Spring 加载 *.properties 文件的源码分析》

通过类图,我们可以看到,PropertyPlaceholderConfigurer 实现了 BeanFactoryPostProcessor 接口,该接口中只有一个接口方法,通过方法注释我么可以了解到:
(1)、该接口可在 bean 实例化之前修改 Application Context ;
(2)、可以加载所有的bean定义,但是bean还没有并实例化;
(3)、允许通过对期待实例化的 Bean 属性进行覆盖和添加。
(4)、BeanFactoryPostProcessor 是一个特别的接口,当Spring加载任何实现了这个接口的bean的配置时,都会在工厂载入所有bean的配置之后执行 postProcessBeanFactory 方法。

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;

}

2、属性资源加载器
(1)、PropertyResourceConfigurer # postProcessBeanFactory

/**
 * {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and
 * {@linkplain #processProperties process} properties against the given bean factory.
 * @throws BeanInitializationException if any properties cannot be loaded
 */
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    try {
        Properties mergedProps = mergeProperties();//加载配置文件到properties

        // Convert the merged properties, if necessary.
        convertProperties(mergedProps);  //必要则转换合并后的属性

        // Let the subclass process the properties.
        processProperties(beanFactory, mergedProps);// 子类初始这些属性,例如:PropertyPlaceholderConfigurer 
    }
    catch (IOException ex) {
        throw new BeanInitializationException("Could not load properties", ex);
    }
}

(2)、PropertiesLoaderSupport # mergeProperties 完成属性文件的读取。
通过源代码分析我们可以看出来,如果 property 中的属性key存在重复的清苦,则后加载的会覆盖先加载的key值!!!
/**
* Return a merged Properties instance containing both the
* loaded properties and properties set on this FactoryBean.
*/
protected Properties mergeProperties() throws IOException {
Properties result = new Properties();

    if (this.localOverride) {
        // Load properties from file upfront, to let local properties override.
        loadProperties(result);
    }

    if (this.localProperties != null) {
        for (Properties localProp : this.localProperties) {// 多个属性文件循环读取,key 相同则覆盖
            CollectionUtils.mergePropertiesIntoMap(localProp, result);
        }
    }

    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.isInfoEnabled()) {
                logger.info("Loading properties file from " + location);
            }
            try {
                PropertiesLoaderUtils.fillProperties(
                        props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister);// 将配置文件中的属性读入到 props 中
            }
            catch (IOException ex) {
                if (this.ignoreResourceNotFound) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Could not load properties from " + location + ": " + ex.getMessage());
                    }
                }
                else {
                    throw ex;
                }
            }
        }
    }
}

3、属性读取工具类
PropertiesLoaderUtils # fillProperties 通过IO读取资源文件。
《Spring 加载 *.properties 文件的源码分析》

4、AbstractBeanFactory 解析占位符

AbstractBeanFactory # resolveEmbeddedValue

public String resolveEmbeddedValue(String value) {
    String result = value;
    for (StringValueResolver resolver : this.embeddedValueResolvers) {
        if (result == null) {
            return null;
        }
        result = resolver.resolveStringValue(result);
    }
    return result;
}

5、DefaultListableBeanFactory # resolveDependency 方法
  
《Spring 加载 *.properties 文件的源码分析》

6、AutowiredAnnotationBeanPostProcessor 属性自动注入
 
《Spring 加载 *.properties 文件的源码分析》

通过反射的方式将占位符的值得以替换。

以上即是整个 properties 文件从加载到替换属性的全过程。

    原文作者:Spring Cloud
    原文地址: https://blog.csdn.net/dalinsi/article/details/53037957
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞