Spring之PropertyPlaceholderConfigurer源码分析

一 引言

  第一部分先说说在Spring下,怎么使用PropertyPlaceholderConfigurer及其原理。

  第二部分再说说SpringBoot下,新的PropertySourcesPlaceholderConfigurer

二 代码示例

  如果我们想在代码中使用@Value之类的注解,就需要在Spring的配置文件里这么写

<bean id="propertyPlaceholderConfigurer" class="org.springframework,beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>jdbc.properties<value/>
        </list>
    </property>
</bean>

要分析的重点就是 PropertyPlaceholderConfigurer,而它的父类是 PropertyResourceConfigurer

public abstract class PropertyResourceConfigurer extends PropertiesLoaderSupport
        implements BeanFactoryPostProcessor, PriorityOrdered

  PropertyResourceConfigurer是一个BeanFactoryPostProcessor,熟悉Spring的都知道,Spring容器会首先初始化BeanFactoryPostProcessor,当其他的Bean还是BeanDefinition的时候,BeanFactoryPostProcessor就已经初始化好了。

  BeanFactoryPostProcessor是接口,它有定义的方法 void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

  我们看看在PropertyResourceConfigurer中是怎么实现的

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);
        }
    }
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) {
                CollectionUtils.mergePropertiesIntoMap(localProp, result);
            }
        }

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

        return result;
    }
locations就是我们输入的多个配置文件,它会遍历每一个配置文件把里面的属性读出来,
protected void loadProperties(Properties props) throws IOException {
        if (this.locations != null) {
            for (Resource location : this.locations) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Loading properties file from " + location);
                }
                try {
                    PropertiesLoaderUtils.fillProperties(
                            props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister);
                }

继续看子类实现的processProperties

  PropertyPlaceholderConfigurer.processProperties

protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
            throws BeansException {

        StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
        doProcessProperties(beanFactoryToProcess, valueResolver);
    }

  

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);//处理BeadDefinition里的属性,BD中的属性有类型,比如Map list 引用类型等,如果在此时能够把值填充上就进行处理
                }
                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);//这里很关键,因为@Value就是依靠的内部ValueResolver
    }

最后的那句addEmbeddedValueResolver就能够关联到对于@Value的处理了

三 SpringBoot中的 PropertySourcesPlaceholderConfigurer

  在spring-boot-autoconfigure.jar里的spring.factories中第29行有这么一段

org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,

  

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class PropertyPlaceholderAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

}

也就是说他会自动的把 PropertySourcesPlaceholderConfigurer,加到Spring容器中,而不是想过去那样需要在配置文件里写明。

同时在Springboot环境下,也不一定非要指定用户自己的属性配置文件比如dbconfig.properties。而是可以直接写到application.yml,或者application.properties中

这是因为springBoot在启动是会指定initializers,我记得有五个initializer,其中有一个专门负责解析classpath下的application.yml,application.properties和 classpath/config/下的application.yml,application.properties一共四个文件。不是说这四个文件一定要都存在,而是如果存在就把环境变量,jar包的启动参数,连同这四个可能的文件里的属性读取出来,共同构造成environment变量。

  而实现了EnvironmentAware接口。

public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerSupport implements EnvironmentAware

  在Spring容器启动并且在执行自动装配的时候就会调用

  AutoConfigurationImportSelector.selectImports

public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return StringUtils.toStringArray(configurations);
    }

  所以在springBoot中是可以直接读出来yml中的属性的

  

  

原文地址:https://www.cnblogs.com/juniorMa/p/14323883.html