Spring Beans的初始化流程
ClassPathXmlApplicationContext初始化beand的流程
Spring 容器对 Singleton bean 从初始化并注册到当前容器的与之相关的主要有两个流程,
1.解析 bean definitions 并注册
2.从1 中找到所有的已注册的 singleton bean definitions,遍历,实例化得到 Singleton beans
bean definitions
bean definitions就是用来描述 bean 配置中的 element 元素的
例如
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.shangyang" />
<bean name="jane" class="org.shangyang.spring.container.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>
可以看到,上面有三个 element
1., root element
2.context:component/, component-scan element
3., bean element
在配置文件 beans.xml 被 Spring 解析的过程中,每一个 element 将会被解析为一个 bean definition 对象缓存在 Spring 容器中;
➥ 需要被描述为 bean definitions 的配置对象主要分为如下几大类,
- xml-based,解析 xml beans 的情况;
- 使用 @Autowired、@Required 注解注入 beans 的解析情况;
需要特殊处理并解析的元素 context:annotation-config/ - 使用 @Component、@Service、@Repository,@Beans 注解注入 beans 的解析情况;需要特殊处理并解析的元素 context:annotation-scan/
一、首选初始化得到 BeanFactory 实例 DefaultListableBeanFactory,用来注册解析配置后生成的 bean definitions;
二、然后通过 XmlBeanDefinitionReader 解析 Spring XML 配置文件
根据用户指定的 XML 文件路径 location,进行解析并且得到 Resource[] 对象,具体参考 step 1.1.3.3.1.1 getResource(location) 步骤;这里,对其如何通过 location 得到 Resource[] 对象做进一步分析
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// a class path resource (multiple resources for same name possible)
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
return findPathMatchingResources(locationPattern);
}
else {
// all class path resources with the given name
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
// Only look for a pattern after a prefix here
// (to not get fooled by a pattern symbol in a strange prefix).
int prefixEnd = locationPattern.indexOf(":") + 1;
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
}
else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
三、从2 解析得到的 Resource 实例 resource 对应的是 beans.xml 的配置信息,从 step 1.1.3.3.1.2 loadBeanDefinitions 开始将会对 resource 既是 beans.xml 中的元素依次进行解析;首先生成对应 beans.xml 的 org.w3c.Document 对象实例 document,见 step 1.1.3.3.1.2.2.1,其次得到解析 document 对象的 BeanDefinitionDocumentReader 实例 documentReader,将当前的 Resource 对象封装为 XmlReaderContext 实例 xmlReaderContext,最后通过 documentReader 开始正式解析 document 对象得到 bean definitions 并将其注册到当前的 beanFactory 实例中
当完成上述三个步骤以后,将进入 register bean definitions process 流程
1.从 document 对象中获得了 Root 实例 root
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
首先,判断 root 元素的 namespace 对应的是不是 default namespace,若不是,将进入 parse custom element
遍历 root 元素下的所有 element,
若 element 的 namespace 是 default namespace,将进入 parse default element 流程;
比如当前 element 是普通的
若 element 的 namespace 不是 default namespace,将进入 parse custom element 流程;
比如当前 element 是 context:annotation-config/ 或者是 context:component-scan/
parse default element process
解析 bean element 流程
➥ 首先,通过 BeanDefintionParserDelegate 对象解析该 element,得到一个 BeanDefinitionHolder 对象 bdHolder 实例;该解析过程中会依次去解析 bean id, bean name, 以及相关的 scope, init, autowired model 等等属性;
➥ 其次,对 bean definition 进行相关的修饰操作
常规步骤
1.遍历当前 element 中的所有 attributes,依次得到 atttribute node
2.取得 node 所对应的 namespace URI,并判断该 namespace 是否是 custom namespace,如果是 custom namespace,那么正式进入对该 attribute node 的修饰过程,如下所述;
attribute node 的修饰过程
假设,我们当前的 attribute node 为 p:spouse-ref=”jane”,看看该属性是如何被解析的,
首先,通过 node namespace 得到对应的 NamespaceHandler 实例 handler
通过 xmlns:p=”http://www.springframework.org/schema/p" 得到的 NamespaceHandler 为 SimplePropertyNamespaceHandler 对象;
其次,调用 SimplePropertyNamespaceHandler 对象对当前的元素进行解析;
可以看到,前面的解析并没有什么特殊的,从元素 p:spouse-ref=”jane” 中解析得到 propery name: spouse-ref,property value: jane;但是后续解析,比较特殊,需要处理 REF_SUFFIX 的情况了,也就是当 property name 的后缀为 -ref 的情况,表示该 attribute 是一个 ref-bean 属性,其属性值引用的是其它的 bean 实例,所以呢,这里将其 property value 封装为了一个 RuntimeBeanReference 对象实例,表示将来在解析该 property value 为 Java Object 的时候,需要去初始化其引用的 bean 实例 jane,然后注入到当前的 property value 中;
最后,将解析后得到的 bean definition 封装在 bean definition holder 对象中进行返回;
➥ 最后,注册 bean definition;
参考
https://www.shangyang.me/2017/04/07/spring-core-container-sourcecode-analysis-register-bean-definitions/