Spring之启动过程obtainFreshBeanFactory()
1.refresh流程
refresh
是spring启动的关键方法,refresh启动过程中,先要得到beanFactory 以及 需要交给beanFactory管理的bean。
在refresh时,prepareRefresh
后,马上就调用了obtainFreshBeanFactory
创建beanFactory以及扫描bean信息(beanDefinition),并通过BeanDefinitionRegistry
注册到容器中。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//后面省略
....
}
}
2.obtainFreshBeanFactory扫描bean定义信息并注册
这里以通过XML配置spring来举例。
- 创建DefaultListableBeanFactory,设置是否支持beanDefinition重写以及是否支持循环引用。
- 载入beanDefinition
- 得到所有spring相关的xml,将其转为Document对象。
- 解析Document,判断node是否属于beans(
http://www.springframework.org/schema/beans
)这个命名空间(Namespace)。针对默认命名空间(beans)和非默认空间,有不同的处理。- 处理默认命名空间相关node:
import
,alias
,bean
,beans
4种node。 - 处理非默认命名空间相关node,如:
<context:annotation-config/>
、<context:component-scan base-package="xx"/>
、<mongo:mongo-client/>
- 注册beanDefinition
- 处理默认命名空间相关node:
obtainFreshBeanFactory重要流程
2.必须了解的类
2.1 beanDefinition
包含欲交给spring管理的bean信息
扫描了一个对象的信息,包括class,property等。
2.2 NamespaceHandler
注册自定义解析类
该接口的作用:注册可解析xml中自定义的标签Parser。
实现类类名与命名空间名字对应。
-
常见的NamespaceHandler实例:ContextNamespaceHandler
很明显能看出有常用的<context:annotation-config/>
、<context:component-scan base-package="xx"/>
、<context:property-placeholder/>
对应的解析类。public class ContextNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); } }
2.3 BeanDefinitionParser接口
配合DefaultBeanDefinitionDocumentReader
去解析xml中自定义的标签。
接口需要实现BeanDefinition parse(Element element, ParserContext parserContext);
方法,该方法返回一个beanDefination。但是实际都没有处理这个返回值, 在parse内部就解析beanDefination并注册到beanDefinationMap中了。
实现类与节点名称对应。
2.4 BeanDefinitionParser与ContextNamespaceHandler的关系
类似于 xHandler.put(“a”,parserA); Handler.put(“b”,parserB);
当处理非默认命名空间的节点时,如x
命名空间中的<a>
节点,使用xHandler.get(“a”).parse来解析并注册beanDefinition
4.处理默认命名空间
以处理<bean>
为例,调用装饰器去解析delegate.parseBeanDefinitionElement(ele)
将bean上的属性,如id,name,property,class等,并返回一个BeanDefinition对象。然后调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())
将其注册到beanDefinitionMap中。
5.处理非默认命名空间
根据命名空间得到对应的NamespaceHandler
,然后调用节点对应的parser去解析注册beanDefinition.
如context命名空间中解析<context:component-scan base-package="com.gkwind.xxx"/>
的ComponentScanBeanDefinitionParser
这里解析有点复杂,但思路很简单: 从xml文件对应的element中处理对应的node。将node的属性装配成beanDefinition或者利用其属性代表的特殊含义去注册beanDefinition.
如这里<context:component-scan base-package="com.gkwind.xxx"/>
),就表明需要:
1.扫描这个包下面的所有class。
2.将符合filter的class包装为beanDefinition并注册到beanDefinitionMap中。
如:含有@Component等注解的class
6.debug的时候遇到的问题
Spring启动时DefaultNamespaceHandlerResolver如何初试化?
DefaultNamespaceHandlerResolver的toString方法会触发getHandlerMappings
,而使用idea debug的时候会调用toString引起handlerMapping初始化,导致看到handlerMapping莫名的出现值。
@Override
public String toString() {
return "NamespaceHandlerResolver using mappings " + getHandlerMappings();
}
7.整体流程
Spring启动时DefaultNamespaceHandlerResolver如何初试化?
Spring之NamespaceHandler与BeanDefinitionParser