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

栏目: Java · 发布时间: 5年前

内容简介:本文解析Spring源码,回答以下几个问题:1、Spring的Property配置加载和使用过程?2、Spring内置的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. 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的初始化和使用过程,就非常清晰了。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

腾讯方法

腾讯方法

潘东燕、王晓明 / 机械工业出版社 / 2014-12-11 / 39.00

这是国内第一本深度讲述腾讯产品研发与团队转型的书。本书介绍了腾讯三个不同生命周期的产品的开发过程,包括如何踏足新领域开发新产品;如何救活一个即将半路夭折的产品;如何让一个老产品持续盈利。本书呈现了互联网产品开发时会遇到普遍问题和解决方法,涉及大企业如何内部创业,并迅速组建新的项目团队;如何实现跨部门的合作;在面临新团队和紧急开发任务时如何提高团队沟通效率;在产品研发方面,如何定位产品、如何敏捷开发......一起来看看 《腾讯方法》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具