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來舉例。

  1. 創建DefaultListableBeanFactory,設置是否支持beanDefinition重寫以及是否支持循環引用。
  2. 載入beanDefinition
    • 得到所有spring相關的xml,將其轉爲Document對象
    • 解析Document,判斷node是否屬於beans(http://www.springframework.org/schema/beans)這個命名空間(Namespace)。針對默認命名空間(beans)和非默認空間,有不同的處理。
      • 處理默認命名空間相關node:import,alias,bean,beans4種node。
      • 處理非默認命名空間相關node,如:<context:annotation-config/><context:component-scan base-package="xx"/><mongo:mongo-client/>
      • 註冊beanDefinition

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

發佈了118 篇原創文章 · 獲贊 47 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章