spring IoC源碼分析 (3)Resource解析

定義好了Resource之後,看到XmlFactoryBean的構造函數

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}
跟到XmlBeanDefinitionReader 的 loadBeanDefinitions(EncodedResource encodedResource) 方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }
       
        //爲了線程能使用同一個 currentResources  ,從這裏可以看出作者對 ThreadLocal 的理解深刻
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<EncodedResource>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        //這裏其實就是爲了避免循環加載,如果重複加載了相同的文件就會拋出異常
        //這裏看了半天才明白這個set的意圖,蛋疼啊
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected recursive loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.set(null);
            }
        }
    }
其實關鍵方法是 doLoadBeanDefinitions(inputSource, encodedResource.getResource()) ,但是上面的註釋絕對花了我將近1個鐘頭才理解作者想表達的意思,剛開始一看到ThreadLocal 就想到線程安全。從上面可以看到關鍵方法是doLoadBeanDefinitions,這個方法的關鍵代碼其實就幾行
try {
            //判斷xml文件是DTD還是XSD樣式,如果沒定義將使用XSD
            int validationMode = getValidationModeForResource(resource);
            Document doc = this.documentLoader.loadDocument(
                    inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
            return registerBeanDefinitions(doc, resource);
        }

看下getValidationModeForResource 一路跟蹤下來到XmlValidationModeDetector的detectValidationMode方法
while ((content = reader.readLine()) != null) {
				content = consumeCommentTokens(content);
				if (this.inComment || !StringUtils.hasText(content)) {
					continue;
				}
				if (hasDoctype(content)) {
					isDtdValidated = true;
					break;
				}
				if (hasOpeningTag(content)) {
					// End of meaningful data...
					break;
				}
			}

就是分析下xml文件裏有沒有DOCTYPE關鍵字,沒有的話就認爲是xsd格式的。然後就到了documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); 這個方法
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
		if (logger.isDebugEnabled()) {
			logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
		}
		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
		return builder.parse(inputSource);
	}

其實就是使用DocumentBuilderFactory 去解析xml,這塊不怎麼熟,查了下網上的介紹也沒太詳細的。
接着跟蹤到registerBeanDefinitions 方法,關鍵部分:documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;

		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();

		BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);

		preProcessXml(root);
		parseBeanDefinitions(root, delegate);
		postProcessXml(root);
	}
preProcessXml和postProcessXml是提供擴展用得,這裏沒有具體實現,從字面上理解也是給之類提供預處理和事後處理用的。具體解析工作是parseBeanDefinitions,跟蹤到private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)  這個方法,我們主要關注對bean的解析,所以直接看processBeanDefinition(ele, delegate)
/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
/* BeanDefinitionHolder是BeanDefinition對象的封裝類,封裝了 BeanDefinition,Bean的名字和別名。用它來完成向IoC容器註冊。 BeanDefinitionParserDelegate對XML元素的信息按照Spring的Bean規則進行解析*/
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}
先來看delegate.parseBeanDefinitionElement(ele) 方法,BeanDefinitionParserDelegate這個類 裏包含了對各種Spring Bean定義規則的處理。比如我們最熟悉 的對Bean元素的處理是怎樣完成的,也就是怎樣處理在XML定義文件中出現的 <bean></bean>這個最常見的元素信息。在這裏會看到對那些熟悉的BeanDefinition定義的處 理,比如id、name、aliase等屬性元素。把這些元素的值從XML文件相應的元素的屬性中讀 取出來以後,設置到生成的BeanDefinitionHolder中去。這些屬性的解析還是比較簡單的。 對於其他元素配置的解析,比如各種Bean的屬性配置,通過一個較爲複雜的解析過程,這個 過程是由parseBeanDefinitionElement來完成的。解析完成以後,會把解析結果放到 BeanDefinition對象中並設置到BeanDefinitionHolder中去。其實就是根據spring自己對xml文件的定義進行解析。這個 BeanDefinition數據對象中封裝的數據大多都是與<bean>定義相關的,也有很多就是我們在定義Bean時看到 的那些Spring標記,比如常見的init-method、destroy-method、factory-method,等等,這個 BeanDefinition數據類型是非常重要的,它封裝了很多基本數據,這些基本數據都是IoC容器 需要的。有了這些基本數據,IoC容器才能對Bean配置進行處理,才能實現相應的容器特性。spring對資源文件的解析和加載基本到此,下一篇繼續分析spring對bean的獲取


貼一張spring 內部調用的圖先




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章