内容简介:本文解析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的初始化和使用过程,就非常清晰了。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- spring bean初始化过程
- Java的类/实例初始化过程
- 不可逆的类初始化过程
- Mybatis源码解读-初始化过程详解
- 带你了解 Java 的初始化与清理过程!!!
- vue 源码学习(二) 实例初始化和挂载过程
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
MD5 加密
MD5 加密工具
XML、JSON 在线转换
在线XML、JSON转换工具