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