Spring的Property配置加载和使用过程及Environment的初始化过程
2018年01月17日


本文解析Spring源码,回答以下几个问题:

1、Spring的Property配置加载和使用过程?

2、Spring内置的Environment的初始化和使用过程?

3、常见Spring的Property相关类的关系?


对于Spring加载Property配置,有如下几个类:(按层级展示)

PropertiesLoaderSupport

  PropertiesFactoryBean !!!直接配置在xml里面,参见(1)

  PropertyResourceConfigurer (implements BeanFactoryPostProcessor)

    PlaceholderConfigurerSupport

      PropertyPlaceholderConfigurer !!!直接配置在xml里面(2)

      PropertySourcesPlaceholderConfigurer !!!以标签的形式配置在xml里面

例如:

(1)

<bean id="propBean" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
  <property name="locations" value="classpath:jdbc.properties"/>  
</bean>

使用方式:

@Value("#{propBean['filePath']}")或者@Value("#{propBean.filePath}")


(2)

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations" value="classpath:jdbc.properties"/>  
</bean>

等价于<context:property-placeholder location="classpath:jdbc.properties"/>

(参见PropertyPlaceholderBeanDefinitionParser)

或者

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="properties" ref="propBean" />
</bean>

使用方式:

@Value("${filePath}")



下面几者的关系?

PropertySourcesPlaceholderConfigurer

PropertySource

PropertyResolver

AbstractEnvironment

首先,PropertySource其实就是包装的具体配置,跟Properties差不多。

而PropertyResolver,就是用于对PropertySource进行特殊处理,比如解析holder、转换值的类型等。


Spring启动时,默认会new一个StandardEnvironment,这个类里面就默认添加了两个PropertySource(SystemProperties和SystemEnvironment,分别对应System.getenv和System.getProperty)

注意,可能是为了使用方便,Environment实现了PropertyResolver接口。


所以说,它们的关系实际上是,Environment启动时默认添加了一些PropertySource。

启动流程是这样的(AbstractApplicationContext):

@Override
public void refresh() throws BeansException {
    prepareRefresh();

    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    prepareBeanFactory(beanFactory);

    postProcessBeanFactory(beanFactory);

    // Invoke factory processors registered as beans in the context.
    invokeBeanFactoryPostProcessors(beanFactory);

    // Register bean processors that intercept bean creation.
    registerBeanPostProcessors(beanFactory);

    initMessageSource();
    initApplicationEventMulticaster();

    onRefresh();

    // Check for listener beans and register them.
    registerListeners();

    // Instantiate all remaining (non-lazy-init) singletons.
    finishBeanFactoryInitialization(beanFactory);

    // Last step: publish corresponding event.
    finishRefresh();
}

可以看到,AbstractApplicationContext.refresh() >> prepareRefresh()

然后prepareRefresh会调用createEnvironment()和initPropertySources()

这两个方法实际上是一体的,子类可以重写,其作用就是初始化Environment,并初始化Environment中的PropertySources,以AbstractRefreshableWebApplicationContext为例,相关代码如下:

@Override
protected ConfigurableEnvironment createEnvironment() {
    return new StandardServletEnvironment();
}
@Override
protected void initPropertySources() {
    ConfigurableEnvironment env = getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env)
          .initPropertySources(this.servletContext, this.servletConfig);
    }
}

这两个方法是紧挨着执行的,如果写成下面这样,也是等价的:

@Override
protected ConfigurableEnvironment createEnvironment() {
    StandardServletEnvironment env = new StandardServletEnvironment();
    env.initPropertySources(this.servletContext, this.servletConfig);
    return env;
}
@Override
protected void initPropertySources() {
}

另外,前面提到了new StandardEnvironment()的时候,代码如下:

private final ConfigurablePropertyResolver propertyResolver =
			new PropertySourcesPropertyResolver(this.propertySources);

public AbstractEnvironment() {
    customizePropertySources(this.propertySources);
}

@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
    propertySources.addLast(
      new MapPropertySource("systemProperties", getSystemProperties()));
    propertySources.addLast(
      new SystemEnvironmentPropertySource("systemEnvironment", getSystemEnvironment()));
}

也就是说new出来的Environment默认是包含 System.getenv和System.getProperty 的。

同时可以看到,Environment中使用的PropertyResolver正是PropertySourcesPropertyResolver,且为private final的。


好了,看到这里,其实就知道了:如果要给Spring启动初始化之前添加额外的配置,最早的地方就是拿到AbstractEnvironment.propertySources,然后添加PropertySource,举例如下:

env.getPropertySources().addLast(new MapPropertySource("myProperties", myMap));

但是,要拿到env,就需要拿到 Application,通过 app.getEnvironment()获得。


能不能通过配置的方式,将自定义的配置传递给Environment呢?

接下来,就要学习 PropertyPlaceholderConfigurer 和 PropertySourcesPlaceholderConfigurer了。


首先,这两个类,名字差不多,功能也类似,前者在Spring 3.1以前是标配,自从3.1以后,就换成后者了。

我找到了这个类PropertyPlaceholderBeanDefinitionParser,代码如下,它的作用是:Parser for the {@code <context:property-placeholder/>} element.(这个标签是在spring-context.xsd里面定义的)

@Override
protected Class<?> getBeanClass(Element element) {
    // As of Spring 3.1, the default value of system-properties-mode has changed from
    // 'FALLBACK' to 'ENVIRONMENT'. This latter value indicates that resolution of
    // placeholders against system properties is a function of the Environment and
    // its current set of PropertySources.
    if ("ENVIRONMENT".equals(element.getAttribute("system-properties-mode"))) {
        return PropertySourcesPlaceholderConfigurer.class;
    }

    // The user has explicitly specified a value for system-properties-mode: revert to
    // PropertyPlaceholderConfigurer to ensure backward compatibility with 3.0 and earlier.
    return PropertyPlaceholderConfigurer.class;
}

根据代码注释可知,从3.1以后,<context:property-placeholder/>标签的system-properties-mode属性默认值改成了ENVIRONMENT,

那么默认就是 new的 PropertySourcesPlaceholderConfigurer类,而3.1以前用的是PropertyPlaceholderConfigurer。

找到spring-context.xsd,可以看到下面一句,证实了默认值。

<xsd:attribute name="system-properties-mode" default="ENVIRONMENT">

当然,你也可以继续使用PropertyPlaceholderConfigurer,如下:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations" value="classpath:jdbc.properties"/>  
</bean>

真正等价于:

<context:property-placeholder location="classpath:jdbc.properties" system-properties-mode="old" />


下面,来看看通过PropertySourcesPlaceholderConfigurer加载的配置,是如何在Spring bean初始化时使用的。

接着看上面的refresh()执行流程,

AbstractApplicationContext.invokeBeanFactoryPostProcessors() >> 

  PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()

在这个方法中,它会找出所有BeanFactoryPostProcessor然后执行其接口方法:void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory),代码如下

if (beanFactory instanceof BeanDefinitionRegistry) {
    invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
} else {
    invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}

// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
String[] postProcessorNames = beanFactory.getBeanNamesForType(
                                BeanDefinitionRegistryPostProcessor.class, true, false);
...
sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

可见,执行BeanFactoryPostProcessor的过程中,是分了顺序优先级的,beanFactoryPostProcessors优先执行,beanFactoryPostProcessors是从AbstractApplicationContext里面传过来的,可以通过AbstractApplicationContext.addBeanFactoryPostProcessor来添加。

其他的BeanFactoryPostProcessor是从 beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class) 而来的,这就包括了

我们的PropertySourcesPlaceholderConfigurer,但是PropertyResourceConfigurer是最低优先级的,文档上说等价于 nonOrderedPostProcessors,所以它会最后执行。


如此,关系就清楚了:Spring启动时在 refresh() 的过程中 ,在invokeBeanFactoryPostProcessors的末尾,会调用 PropertySourcesPlaceholderConfigurer.postProcessBeanFactory() 对bean进行预处理。


然后我们再看 PropertySourcesPlaceholderConfigurer.postProcessBeanFactory() 的内部执行过程:

postProcessBeanFactory() >> protected processProperties( beanFactory, propertyResolver) >> 

   PlaceholderConfigurerSupport.doProcessProperties( valueResolver )

这个 最终的 valueResolver 来源于上一级的  propertyResolver,而propertyResolver是在 postProcessBeanFactory()方法内部 new出来的,整个方法如下所示:

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    if (this.propertySources == null) {
        this.propertySources = new MutablePropertySources();
        if (this.environment != null) {
            this.propertySources.addLast(
                new PropertySource<Environment>("environmentProperties", this.environment) {
                    @Override
                    public String getProperty(String key) {
                        return this.source.getProperty(key);
                    }
                }
            );
        }
        PropertySource<?> localPropertySource =
                    new PropertiesPropertySource("localProperties", mergeProperties());
        if (this.localOverride) {
            this.propertySources.addFirst(localPropertySource);
        }
        else {
            this.propertySources.addLast(localPropertySource);
        }
    }

    processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
    this.appliedPropertySources = this.propertySources;
}

由此可知,new PropertySourcesPropertyResolver,使用的PropertySources来自于this.environment,

即PropertySourcesPlaceholderConfigurer.environment,这个environment怎么来的呢,这个最终来源应该就是AbstractApplicationContext。

所以说,这个PropertySourcesPropertyResolver实际上包含了environmentProperties和localProperties,而localProperties来源于这个方法mergeProperties(),源码如下:

protected Properties mergeProperties() throws IOException {
    Properties result = new Properties();
    if (this.localOverride) {
        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;
}


如果我们想给PropertySourcesPropertyResolver加配置,就可以重新这个mergeProperties()或者loadProperties(),但是注意,这个配置的优先级,没有Environment里面的配置的优先级高,而且Spring框架内部某些优先级高的地方只用到了Environment,不过对应用层来说,重写这个mergeProperties()应该是够用了。


如果是使用的老版本的PropertyPlaceholderConfigurer,它的源代码如下:

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){
    try {
        Properties mergedProps = mergeProperties();
        // Convert the merged properties, if necessary.
        convertProperties(mergedProps);
        // Let the subclass process the properties.
        processProperties(beanFactory, mergedProps);
    }
}

大同小异,具体不再多说。



总结:

     经过上面的源码分析,对于Spring的Property配置加载和使用过程,以及Spring内置的Environment的初始化和使用过程,就非常清晰了。