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