【死磕 Spring】----- IOC 之 注冊 BeanDefinition


原文出自:http://cmsblogs.com

獲取 Document 對象后,會根據該對象和 Resource 資源對象調用 registerBeanDefinitions() 方法,開始注冊 BeanDefinitions 之旅。如下:

	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

首先調用 createBeanDefinitionDocumentReader() 方法實例化 BeanDefinitionDocumentReader 對象,然后獲取統計前 BeanDefinition 的個數,最后調用 registerBeanDefinitions() 注冊 BeanDefinition。

實例化 BeanDefinitionDocumentReader 對象方法如下:

	protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
		return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
	}

注冊 BeanDefinition 的方法 registerBeanDefinitions() 是在接口 BeanDefinitionDocumentReader 中定義,如下:

	void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
			throws BeanDefinitionStoreException;

從給定的 Document 對象中解析定義的 BeanDefinition 並將他們注冊到注冊表中。方法接收兩個參數,待解析的 Document 對象,以及解析器的當前上下文,包括目標注冊表和被解析的資源。其中 readerContext 是根據 Resource 來創建的,如下:

	public XmlReaderContext createReaderContext(Resource resource) {
		return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
				this.sourceExtractor, this, getNamespaceHandlerResolver());
	}

DefaultBeanDefinitionDocumentReader 對該方法提供了實現:

	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);
	}

調用 doRegisterBeanDefinitions() 開啟注冊 BeanDefinition 之旅。

	protected void doRegisterBeanDefinitions(Element root) {
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

		if (this.delegate.isDefaultNamespace(root)) {
		      // 處理 profile
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					if (logger.isInfoEnabled()) {
						logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					return;
				}
			}
		}

      // 解析前處理
		preProcessXml(root);
		// 解析
		parseBeanDefinitions(root, this.delegate);
		// 解析后處理
		postProcessXml(root);

		this.delegate = parent;
	}

程序首先處理 profile屬性,profile主要用於我們切換環境,比如切換開發、測試、生產環境,非常方便。然后調用 parseBeanDefinitions() 進行解析動作,不過在該方法之前之后分別調用 preProcessXml()postProcessXml() 方法來進行前、后處理,目前這兩個方法都是空實現,交由子類來實現。

	protected void preProcessXml(Element root) {
	}
	
	protected void postProcessXml(Element root) {
	}

parseBeanDefinitions() 定義如下:

	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);
		}
	}

最終解析動作落地在兩個方法處:parseDefaultElement(ele, delegate)delegate.parseCustomElement(root)。我們知道在 Spring 有兩種 Bean 聲明方式:

  • 配置文件式聲明:<bean id="studentService" class="org.springframework.core.StudentService"/>
  • 自定義注解方式:<tx:annotation-driven>

兩種方式的讀取和解析都存在較大的差異,所以采用不同的解析方法,如果根節點或者子節點采用默認命名空間的話,則調用 parseDefaultElement() 進行解析,否則調用 delegate.parseCustomElement() 方法進行自定義解析。

至此,doLoadBeanDefinitions() 中做的三件事情已經全部分析完畢,下面將對 Bean 的解析過程做詳細分析說明。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM