1. 程式人生 > >Spring啟動流程(二)之Spring載入Bean Definition的流程

Spring啟動流程(二)之Spring載入Bean Definition的流程

繼上篇Spring啟動流程(一)

prepareRefresh()

prepareRefresh();//初始化配置和環境

obtainFreshBeanFactory()

// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}

下面的refreshBeanFactory方法的主要工作:載入applicationContext.xml配置檔案,建立Spring Bean Definition

       @Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

DefaultListableBeanFactory是一個很重要的Bean工廠類,內部儲存了bean definition資訊,即bean的元資料,即描述bean的資訊,這些內容存放在DefaultListableBeanFactory內的不同的容器中(map、list、set);

DefaultListableBeanFactory可以通過後置處理器擴充套件;

loadBeanDefinitions方法傳入DefaultListableBeanFactory,內部是將bean definitions資訊設定到bean factory,如何設定呢?會使用不同的bean definition readers來讀取配置檔案中配置的bean,然後設定到DefaultListableBeanFactory內部的不同資料結構中。

比如常用的XmlBeanDefinitionReader。可以看一下BeanDefinitionReader介面的類繼承層次圖如下。

 

Bean Definition大致建立過程:

1、載入applicationContext.xml配置檔案

2、解析xml

3、將xml中的內容轉化為Spring Bean Definition。

 

下面是一些細節及關鍵類:

1、載入資源配置檔案

AbstractBeanDefinitionReader.java

Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);//通過類載入器在classpath下找到applicationContext配置檔案的絕對路徑

2、解析生成的xml物件轉為Spring BeanDefinition:

DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

依次遍歷所有遇到的元素(空行、換行、註釋、xml節點),但是隻會解析xml節點(node instanceof Element) 。

內部除了namespace是http://www.springframework.org/schema/beans的,其他節點解析都走delegate.parseCustomElement(ele);

        @Nullable
	public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
		String namespaceUri = getNamespaceURI(ele);
		if (namespaceUri == null) {
			return null;
		}
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}

這裡的namespaceHandler是從META-INF/spring-handlers檔案讀取的,之後通過反射初始化使用

不過這裡不管是通過哪個namespaceHandler,都要通過NamespaceHandlerSupport轉發處理,因為所有的namespaceHandler都是繼承NamespaceHandlerSupport,重寫了init方法,init方法只是建立xml節點屬性->BeanDefinitionParser的對映關係。

protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
    this.parsers.put(elementName, parser);
}

接著會從這個對映關係裡找名稱空間處理器然後處理

        @Override
	@Nullable
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		return (parser != null ? parser.parse(element, parserContext) : null);
	}

如果是開啟註解的包掃描配置:

<context:component-scan base-package="com.baby.kim">

會呼叫ComponentScanBeanDefinitionParser,將使用@Service,@Controller等註解的物件轉為beanDefinition。

其他的Context namespace下的處理器還有

 

3、註冊beanDefinition到DefaultListableBeanFactory

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

3.1、最終呼叫

將beanDefinition資訊儲存到DefaultListableBeanFactory的屬性中。

this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);

總結:

這一篇主要介紹了Spring是如何解析和將在bean definition到bean factory中的,

下一篇Spring啟動流程(三)之Bean的例項化將介紹Bean的例項化過程