Spring 占位符placeholder的实现原理

时间:2021-02-23 14:38:27   收藏:0   阅读:0

背景:在使用持久化配置中心时,用到一个组件,该组件支持在代码中直接通过@DynamicValue(“指定的KEY名称”)方式,来直接获取配置的key对应的Value值。

1. 占位符是什么?应用场景有哪些?


占位符格式为${property-name},占位符在运行时,会被替换为propetry-value。一般环境相关的属性会用到占位符,属性以key=value格式定义在xxx.properties文件中,来减少对部署代码的更改。

应用场景有:数据库URLS、密码配置。

<!--    Spring2.5中引入的context namespace,property-placeholder属性配置属性文件location。-->
<context:property-placeholder location="classpath:placeholder.properties"/>
<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

 

2. Spring是如何实现占位符-PropertySourcesPlaceholderConfigurer?


先从UML图认识下PropertySourcesPlaceholderConfigurer,下图中,比较重要的两个接口:BeanFactoryPostProcessor, Ordered。

BeanFactoryPostProcessor 能读取Bean配置元数据、在IOC容器实例化Bean之前修改其配置元数据。即将Bean中的占位符替换掉。

Ordered 能控制多个BeanFactoryPostProcessor的执行顺序。

技术图片

 

围绕着重要接口继续展开UML图如下,从下图可以看出:

BeanFactoryPostProcessor接口是具体实现是在PropertySourcesPlaceholderConfigurer类中,PropertySourcesPlaceholderConfigurer.postProcessBeanFactory方法。

Ordered接口是具体实现在PropertyResourceConfigurer类中,PropertyResourceConfigurer.getOrder方法。

技术图片 

 PropertySourcesPlaceholderConfigurer.postProcessBeanFactory 方法源码解析如下。除了主流程外,流程中出现较多的是各种valueResolver, 见名知意先不用分析,后面可以分析下其设计模式。

技术图片
/**
     * 用configurer的PropertySource集合,来替换Bean定义中的占位符${...}
     * PropertySource包含以下内容:
     * 1.  所有的environment property source,即Environment属性
     * 2.  合并后的local properties,可通过#setLocation\#setLocations\#setProperties\setPropertiesArray等方法指定值
     * 3.  通过调用#setPropertySources方法设置的ProperySources,若该方法被调用environment、local properties会被忽略,这个方法设计目标是为用户提供细粒度的控制。一旦设置,不会添加其他的sources.
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 创建PropertySource, 加载Environment属性、加载local properties
        if (this.propertySources == null) {
            this.propertySources = new MutablePropertySources();
            if (this.environment != null) {
                // 加载Environment属性
                this.propertySources.addLast(
                    new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
                        @Override
                        public String getProperty(String key) {
                            return this.source.getProperty(key);
                        }
                    }
                );
            }
            try {
                // 加载local properties,#mergeProperties()方法,加载了用户指定的属性文件,并进行了合并
                PropertySource<?> localPropertySource =
                    new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
                if (this.localOverride) {
                    this.propertySources.addFirst(localPropertySource);
                }
                else {
                    this.propertySources.addLast(localPropertySource);
                }
            }
            catch (IOException ex) {
                throw new BeanInitializationException("Could not load properties", ex);
            }
        }

        // 处理占位符
        processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
        this.appliedPropertySources = this.propertySources;
    }

    /**
     * 访问给定bean factory的每个类定义,并用propertyResolver解析的值替换占位符${...}
     */
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
        final ConfigurablePropertyResolver propertyResolver) throws BeansException {

        // 可以自己指定placeholder的前缀、后缀,比如从${...} 替换为 $[...]
        propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
        propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
        propertyResolver.setValueSeparator(this.valueSeparator);

        StringValueResolver valueResolver = new StringValueResolver() {
            @Override
            public String resolveStringValue(String strVal) {
                String resolved = ignoreUnresolvablePlaceholders ?
                    propertyResolver.resolvePlaceholders(strVal) :
                    propertyResolver.resolveRequiredPlaceholders(strVal);
                return (resolved.equals(nullValue) ? null : resolved);
            }
        };

        // 处理占位符
        doProcessProperties(beanFactoryToProcess, valueResolver);
    }

    protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
        StringValueResolver valueResolver) {

        // 创建BeanDefinitionVisitor,将指定的valueResolver应用于所有Bean的元数据
        BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

        // 遍历beanFactory中定义的所有bean的名字(不能包含当前BeanFactoryPostProcessor实例)
        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 {
                    // 遍历所有的bean定义中的名称、方法、属性等,替换其中的占位符
                    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);
    }
PropertySourcesPlaceholderConfigurer.java(spring 4.2.7)

PropertyResourceConfigurer.getOrder方法源码解析如下。

技术图片
// 默认最低优先级,等同于未排序
private int order = Ordered.LOWEST_PRECEDENCE;  

/**
* 需要排序的话,设置该值
* @see PriorityOrdered
*/
public void setOrder(int order) {
    this.order = order;
}

@Override
public int getOrder() {
    return this.order;
}
PropertyResourceConfigurer.java(spring 4.2.7)

 

3.整合持久化配置中心&Spring占位符


 

背景:在使用持久化配置中心时,用到了同事写的组件。 该组件支持在代码中直接通过@DynamicValue(“指定的KEY名称”)方式,来直接获取配置的key对应的Value值。

 

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!