zoukankan      html  css  js  c++  java
  • 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中的属性的

      

      

  • 相关阅读:
    日期时间類(DateTime)的应用
    C# 排版快捷鑑
    撷取指定网址中的资料Part1:WebClinet 的用法
    Chart in Web
    Android APK反编译得到Java源代码和资源文件
    iOS 6.0 GM 版全系列固件下载
    IOS判断设备是否已越狱(isJailbroken)
    批量离线下载迅雷快传资源
    Android如何防止apk程序被反编译
    Java接口学习
  • 原文地址:https://www.cnblogs.com/juniorMa/p/14323883.html
Copyright © 2011-2022 走看看