美文网首页Spring core
Spring源码之配置文件加载PropertyPlacehold

Spring源码之配置文件加载PropertyPlacehold

作者: 土豆肉丝盖浇饭 | 来源:发表于2018-09-25 11:42 被阅读74次

    PropertyPlaceholderConfigurer的作用

    这个类的作用是把我们配置文件中或者代码中的${}占位符替换为properties文件中的配置
    使用方式如下

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
            <property name="location" value="classpath:test.properties" />
        </bean>
    

    只需要指定配置文件的路径即可,会自动把ioc容器中的占位符替换为对应配置

    原理

    PropertyPlaceholderConfigurer主要是使用到了Spring的BeanFactoryPostProcessor的postProcessBeanFactory回调。
    这个回调方法会在BeanFactory初始化并且加载好BeanDefinition后执行,但会在所有Bean初始化之前。PropertyPlaceholderConfigurer中回调的主要逻辑就是遍历BeanDefinition,对占位符进行替换。

    我们先看下PropertyPlaceholderConfigurer的类继承图

    可以看到PropertyResourceConfigurer继承了BeanFactoryPostProcessor接口,我们从它的postProcessBeanFactory方法看起

    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);
            }
            catch (IOException ex) {
                throw new BeanInitializationException("Could not load properties", ex);
            }
        }
    

    这边有三个步骤,mergeProperties方法merge操作基本没有,主要是用来从硬盘加载properties文件。
    convertProperties用来对PropertyValue做一些自定义转换,默认是返回原值,所以这个方法忽略即可。
    processProperties是具体的propeties替换逻辑,交给子类实现,PropertyPlaceholderConfigurer的实现如下。

    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
                throws BeansException {
    
            StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
            doProcessProperties(beanFactoryToProcess, valueResolver);
        }
    

    PlaceholderResolvingStringValueResolver封装了通过占位符从配置文件获取对应配置的逻辑,我们基本可以认为{a.b}从配置文件获取a.b=ccc对应的ccc值即可。实际上{a.b}可以更复杂,比如{a.b=c}默认值或者{a.b}.${c.d}这种复杂的情况。

    遍历BeanDefinition对占位符进行替换的逻辑则封装在doProcessProperties方法中,这个实现在父类PlaceholderConfigurerSupport中实现

    protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
                StringValueResolver valueResolver) {
    
            BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
    
            String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
            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 {
                        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封装了操作BeanDefiniton逻辑,对占位符进行替换

    public void visitBeanDefinition(BeanDefinition beanDefinition) {
            visitParentName(beanDefinition);
            visitBeanClassName(beanDefinition);
            visitFactoryBeanName(beanDefinition);
            visitFactoryMethodName(beanDefinition);
            visitScope(beanDefinition);
            visitPropertyValues(beanDefinition.getPropertyValues());
            ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
            visitIndexedArgumentValues(cas.getIndexedArgumentValues());
            visitGenericArgumentValues(cas.getGenericArgumentValues());
        }
    

    可以看到占位符可以出现在以上这些地方。不过我们一般使用在PropertyValue中。

    总结

    其实配置文件替换bean的属性的原理挺简单,就是通过BeanFactoryPostProcessor在bean初始化之前,加载Beanfinition之后对Beanfinition中的占位符进行替换。

    分析这个类的主要目的是,因为Disconf动态配置,就是在这个类的基础上修改实现的。

    相关文章

      网友评论

        本文标题:Spring源码之配置文件加载PropertyPlacehold

        本文链接:https://www.haomeiwen.com/subject/jfpkoftx.html