Spring的Property配置加载和使用过程及Environment的初始化过程

本文解析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.classtruefalse);

...

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的初始化和使用过程,就非常清晰了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值