PropertyPlaceholderConfigurer源码解析

来源:互联网 发布:2017年网络热点事件 编辑:程序博客网 时间:2024/06/07 01:37

本文引自我的个人博客: sunmingshuai.coding.me

这篇文章我们介绍一个常用的BFPP来消除我们心中的对BFPP的陌生感
spring配置文件中 读者一定经常遇到过${xxx}的写法 然后spring会帮我们替换成properties中对应的value值 那么这个变量是什么时候又是被哪个类替换的呢
在文章ClassPathXmlApplicationContext源码解析三:BFPP中我们提到了BFPP的概念 BFPP的全称是BeanFactoryPostProcessor是工厂级别的处理器 它的调用时间点是在将配置文件转化为BeanDefinition之后 加载成bean之前调用的
我们先看下配置文件

<bean id="dev_propertyPlace" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">        <property name="order" value="2"/>        <property name="ignoreUnresolvablePlaceholders" value="true" />        <property name="locations">            <list>                <value>classpath:config-sunms-dev.properties</value>            </list>        </property>    </bean>

在使用这个bfpp之前当然是先获取这个bean 处理一些事情 比如将locations对应的string转化成需要的resource类型
既然是bfpp 那么我们的阅读入口就好找了 PropertyResourceConfigurer##postProcessBeanFactory()方法

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {        try {            //加载properties文件            Properties mergedProps = mergeProperties();            convertProperties(mergedProps);            //替换            processProperties(beanFactory, mergedProps);        }        catch (IOException ex) {            throw new BeanInitializationException("Could not load properties", ex);        }    }

spring的代码一直这么优雅 高度抽象 让人一看就知道要做什么事情
第一步就是加载我们指定的properties文件 第二步默认没有做什么事情
最后一步就是重点了

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

先是构建一个处理类 处理类不过是提供一些方法 定义了诸如前缀${ 后缀} 使后面的代码更简洁 然后交给doProcessProperties处理

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.        // 处理如@Value("${name}")        beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);    }

下面的代码就比较简单了 就是处理所有的BeanDefinitioin 不包含本身 将诸如parentName等属性中的${xxx}变量替换掉

    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());    }

有趣的是最后一行代码

beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);

这句代码把构建好的工具类加入到了beanFactory中 是spring3.0加入的 加入的作用就是处理诸如@Value注解的时候 如果有${xxx}的形式的话 会被这个工具类给替换掉 这个在@Autowired注解工作原理源码解析的时候 我们会再提到