美文网首页
Spring之properties解析

Spring之properties解析

作者: 一只白羊座的猫 | 来源:发表于2021-01-23 10:39 被阅读0次

原文来自个人小站 ( Ariescat's Blog ) :Spring之properties解析

Spring Properties使用的几种方式

  1. 在Java中使用这个 @Value("${ }")注解 读取

  2. 在配置文件中使用 ${ } 读取

解析器注册

  1. context:property-placeholder标签

    <context:property-placeholder location="classpath:*.properties"/>
    

    源码解析:

    public class ContextNamespaceHandler extends NamespaceHandlerSupport {
     @Override
     public void init() {
         registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
         ...省略代码
     }
    }
    

    这个Parser实质上注册了一个org.springframework.context.support.PropertySourcesPlaceholderConfigurer(实现了BeanFactoryPostProcessor的处理器)。

    // PropertySourcesPlaceholderConfigurer.java
     @Override
     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
         if (this.propertySources == null) {
             this.propertySources = new MutablePropertySources();
             if (this.environment != null) {
                 this.propertySources.addLast(
                     // 注意看这里this.environment里包含了propertySources
                     // spring boot的配置都在这里
                     new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
                         @Override
                         @Nullable
                         public String getProperty(String key) {
                             // this.source就是this.environment
                             return this.source.getProperty(key);
                         }
                     }
                 );
             }
             try {
                 // 这一步的mergeProperties()也很重要
                 // xml里的location配置会在这里获取
                 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;
     }
    
  2. PropertyPlaceholderConfigurer类

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:resource1.properties</value>
                <value>classpath:config/resource2.properties</value>
                <value>classpath*:resource3.properties</value>
            </list>
        </property>
    </bean>
    

    原理和 @1 差不多

  3. SpringBoot自动装配

    @EnableAutoConfiguration自动装配中spring.factories文件里包含PropertyPlaceholderAutoConfiguration类,

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

    PropertySourcesPlaceholderConfigurer这个实现了BeanFactoryPostProcessor的处理器注册进去。

    这个类就是 @1 里面的类

  4. @PropertySource

    上面@EnableAutoConfiguration的自动装配只是加载了默认的 ./ (config/) application.properties(.yml,.xml)配置文件,可见@PropertySource灵活一点

    @PropertySource(value={"classpath:mail.properties"})
    public class ReadProperties {
      @Value(value="${mail.username}")
       private String USER_NAME;
    }
    

    区别@ImportResource,这个是可以引入带有<bean>标签的xml文件的(也就是普通的spring配置文件),曾经一度把这个和properties配置搞混。

    源码解析:

    org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass

  5. spring boot特有的@ConfigurationProperties

    源码解析:

    ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization

    这里面有空再研究吧,看着有点头大

@Value 源码解析

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties

此处是Spring统一处理注入依赖的地方,接下来会调用到org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency

    // 这里的AutowireCandidateResolver是QualifierAnnotationAutowireCandidateResolver,
    // 其实就是获取@Value上的注解值
    Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
    if (value != null) {
        if (value instanceof String) {
            String strVal = resolveEmbeddedValue((String) value);
            BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                    getMergedBeanDefinition(beanName) : null);
            value = evaluateBeanDefinitionString(strVal, bd);
        }
        TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
        try {
            return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
        } catch (UnsupportedOperationException ex) {
            // A custom TypeConverter which does not support TypeDescriptor resolution...
            return (descriptor.getField() != null ?
                    converter.convertIfNecessary(value, type, descriptor.getField()) :
                    converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
        }
    }

QualifierAnnotationAutowireCandidateResolver就是我们要找的处理类,它负责处理@Qualifier和@Value两个注解的取值操作。取值结果一般为“${xxx}”,然后执行resolveEmbeddedValue((String) value),走进:

// PropertySourcesPlaceholderConfigurer.java
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
            final ConfigurablePropertyResolver propertyResolver) throws BeansException {

        propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
        propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
        propertyResolver.setValueSeparator(this.valueSeparator);

        StringValueResolver valueResolver = strVal -> {
            // 走进这里!!propertyResolver就是上面postProcessBeanFactory()里面创建出来的
            // 通过这一步就可以获取到properties文件的值
            String resolved = (this.ignoreUnresolvablePlaceholders ?
                    propertyResolver.resolvePlaceholders(strVal) :
                    propertyResolver.resolveRequiredPlaceholders(strVal));
            if (this.trimValues) {
                resolved = resolved.trim();
            }
            return (resolved.equals(this.nullValue) ? null : resolved);
        };

        doProcessProperties(beanFactoryToProcess, valueResolver);
    }

参考:

  1. spring- properties 读取的五种方式
  2. SpringBoot 中 @Value 源码解析

相关文章

网友评论

      本文标题:Spring之properties解析

      本文链接:https://www.haomeiwen.com/subject/wziyzktx.html