定義好了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應該本來就是安全執行緒的,所以推測不是為了安全執行緒 //應該是為了線程能使用同一個 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 就想到安全執行緒,然後就想currentResources 永遠是安全執行緒的啊,丫就這麼被帶坑裡去了。從上面可以看到關鍵方法是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 內部調用的圖先