Spring core

Spring源码之配置文件加载PropertyPlacehold

2018-09-25  本文已影响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动态配置,就是在这个类的基础上修改实现的。

上一篇 下一篇

猜你喜欢

热点阅读