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

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

内容简介:本文解析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的初始化和使用过程,就非常清晰了。


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

查看所有标签

猜你喜欢:

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

Practical Vim, Second Edition

Practical Vim, Second Edition

Drew Neil / The Pragmatic Bookshelf / 2015-10-31 / USD 29.00

Vim is a fast and efficient text editor that will make you a faster and more efficient developer. It’s available on almost every OS, and if you master the techniques in this book, you’ll never need an......一起来看看 《Practical Vim, Second Edition》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具