分析下面這行代碼的運行過程:java
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(「beanFactoryTest.xml」));
在XMLBeanFactory構造方法XmlBeanFactory(Resource resource):ide
@Deprecated @SuppressWarnings({"serial", "all"}) public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } }
跟着this.reader.loadBeanDefinitions(resource);進入XmlBeanDefinitionReader類型的reader屬性提供的loadBeanDefinitions(Resource resource)方法.this.reader.loadBeanDefinitions(resource);這行代碼是整個資源加載的切入點,先來看一下調用時序圖: 函數
從上面時序圖中能夠得知,在加載XML文檔和解析註冊Bean,一直在作準備工做.依據上面的時序圖來分析一下這裏究竟在作什麼準備?this
封裝資源文件.當進入XmlBeanDefinitionReader後首先對參數Resource使用EncodedResource類進行封裝
獲取輸入流.從Resource中獲取對應的InputStream並構造InputSource.
經過構造的InputSource實例和Resource實例繼續調用函數doLoadBeanDefinitions
接下來看一下loadBeanDefinitions函數具體的實現過程:編碼
@Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }
其中EncodedResource的做用:對資源文件的編碼進行處理.其中的主要邏輯體如今getReader方法中,當設置了編碼屬性的時候Spring會使用相應的編碼做爲輸入流的編碼.其中getReader方法以下:code
public Reader getReader() throws IOException { if (this.charset != null) { return new InputStreamReader(this.resource.getInputStream(), this.charset); } else if (this.encoding != null) { return new InputStreamReader(this.resource.getInputStream(), this.encoding); } else { return new InputStreamReader(this.resource.getInputStream()); } }
上面的代碼中構造了一個由編碼(encoding)的InputStreamReader.當構造好EncodedResource對象後,再次轉入了可複用方法loadBeanDefinitions(EncodedResource encodedResource).xml
這個方法內部纔是真正的數據準備階段,也就是時序圖所描述的邏輯:對象
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()); } // 經過屬性來記錄已經加載的資源 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { // 從EncodedResource中獲取已經封裝的Resource對象並再次從Resource中獲取其中的inputStream InputStream inputStream = encodedResource.getResource().getInputStream(); try { // InputSource這個類並非來源於Spring,他的全路徑爲:org.xml.sax.InputSource 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.remove(); } } }
整理一下上面的邏輯:首先對resource參數作封裝,目的是考慮到Resource可能存在編碼要求的狀況,其次,經過SAX讀取XML文件的方式來準備InputSource對象,最後將準備的數據經過參數傳入真正的核心處理部分doLoadBeanDefinitions(inputSource, encodedResource.getResource()).其中doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法實現以下:blog
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
忽略掉上面的異常處理部分,上面的代碼只作了三件事情:ip
獲取對XML文件的驗證模式 加載XML文件,並獲得對應的Document. 根據返回的Document註冊Bean信息. 上面的三個步驟支撐着整個Spring容器部分的實現基礎.