Spring IOC原始碼(三):IOC容器之 建立容器
阿新 • • 發佈:2022-12-11
1、原始碼解析
obtainFreshBeanFactory()方法包含了 Spring的IOC容器 - DefaultListableBeanFactory物件的建立、解析配置檔案中的bean資訊載入至容器中。1 // 建立容器物件DefaultListableBeanFactory,載入xml配置檔案的屬性值到當前工廠中 2 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
1.1、初始化容器 - refreshBeanFactory()
refreshBeanFactory()方法由AbstractApplicationContext的子類AbstractRefreshableApplicationContext具體實現,初始化容器-beanFactory。1 // 重新整理內部工廠 2 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { 3 // 初始化BeanFactory,並讀取XML檔案,將得到的BeanFactory記錄在當前實體的屬性中 4 refreshBeanFactory(); 5 // 返回當前實體的beanFactory屬性 6 return getBeanFactory(); 7 }refreshBeanFactory()的整體流程:
1、建立容器beanFactory
建立DefaultListableBeanFactory物件的容器 - beanFactory(忽略BeanNameAware、BeanFactoryAware、BeanClassLoaderAware介面實現類);2、為beanFactory填充屬性
customizeBeanFactory(beanFactory)是AbstractRefreshableApplicationContext中的方法, 為beanFactory設定屬性。1 // 為beanFactory填充屬性 2 protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { 3 // 如果屬性allowBeanDefinitionOverriding不為空,設定給beanFactory物件相應屬性,是否允許覆蓋同名稱的不同定義的物件allowBeanDefinitionOverriding 與allowCircularReferences 是在createBeanFactory(beanFactory);中做的賦值操作。allowCircularReferences 是在建立DefaultListableBeanFactory物件做的初始化,預設值為true。4 if (this.allowBeanDefinitionOverriding != null) { 5 beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); 6 } 7 // 如果屬性allowCircularReferences不為空,設定給beanFactory物件相應屬性,是否允許bean之間存在迴圈依賴 8 if (this.allowCircularReferences != null) { 9 beanFactory.setAllowCircularReferences(this.allowCircularReferences); 10 } 11 }
1 // 是否允許名稱相同但beanDefinition不同的物件被覆蓋 2 private boolean allowBeanDefinitionOverriding = true;
3、獲取beanDefinition
解析配置檔案中定義的標籤資訊: 預設標籤解析;自定義標籤解析。loadBeanDefinitions(beanFactory);載入bean定義資訊。
1 // 通過XmlBeanDefinitionReader載入beanDefinition 2 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { 3 // Create a new XmlBeanDefinitionReader for the given BeanFactory. 4 // 建立一個xml的beanDefinitionReader,並通過回撥設定到beanFactory中 5 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 6 7 // 為beanDefinitionReader設定環境物件 8 beanDefinitionReader.setEnvironment(this.getEnvironment()); 9 beanDefinitionReader.setResourceLoader(this); 10 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); 11 12 // 初始化beanDefinitionReader物件,並設定配置檔案是否要進行驗證 13 initBeanDefinitionReader(beanDefinitionReader); 14 // 載入BeanDefinitions 15 loadBeanDefinitions(beanDefinitionReader); 16 }上述程式碼是AbstractXmlApplicationContext中的方法,建立了bean定義資訊讀取器,為讀取器填充屬性,最終會呼叫XmlBeanDefinitionReader中的loadBeanDefinitions()方法,核心虛擬碼如下:
1 // 載入beanDefinition資訊 2 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { 3 4 // 通過屬性來記錄已經載入的資源 5 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); 6 if (!currentResources.add(encodedResource)) { 7 throw new BeanDefinitionStoreException( 8 "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); 9 } 10 // 從encodedResource中獲取已經封裝的Resource物件, 再獲取Resource物件的inputStream流資訊 11 try (InputStream inputStream = encodedResource.getResource().getInputStream()) { 12 InputSource inputSource = new InputSource(inputStream); 13 if (encodedResource.getEncoding() != null) { 14 inputSource.setEncoding(encodedResource.getEncoding()); 15 } 16 // 載入beanDefinition核心邏輯,實際從指定的xml中載入beanDefinition 17 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 18 } 19 catch (IOException ex) { 20 throw new BeanDefinitionStoreException( 21 "IOException parsing XML document from " + encodedResource.getResource(), ex); 22 } 23 finally { 24 // 移除已經載入的資源 25 currentResources.remove(encodedResource); 26 if (currentResources.isEmpty()) { 27 this.resourcesCurrentlyBeingLoaded.remove(); 28 } 29 } 30 }可以看到這一步還是載入beanDefinition的準備工作,獲取Resource物件的輸入流inputStream,封裝輸入流並進入載入beanDefinition的核心邏輯doLoadBeanDefinitions()。
1 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 2 throws BeanDefinitionStoreException { 3 4 try { 5 // 獲取xml檔案的document物件,這個解析過程是由documentLoader完成的, 6 Document doc = doLoadDocument(inputSource, resource); 7 // 根據文件的節點資訊封裝成一個個的BeanDefinition物件 8 int count = registerBeanDefinitions(doc, resource); 9 if (logger.isDebugEnabled()) { 10 logger.debug("Loaded " + count + " bean definitions from " + resource); 11 } 12 return count; 13 } 14 ..... 15 catch (Throwable ex) { 16 throw new BeanDefinitionStoreException(resource.getDescription(), 17 "Unexpected exception parsing XML document from " + resource, ex); 18 } 19 }該步驟主要是註冊bean定義資訊的準備工作,獲取配置檔案的document物件,通過String[](配置檔案集合) -string(配置檔案)-Resource[]- resource,最終開始將resource讀取成一個document文件。
1 // 建立document物件的讀取器 2 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { 3 // 對xml的beanDefinition進行解析 4 // 建立BeanDefinitionDocumentReader物件 5 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); 6 int countBefore = getRegistry().getBeanDefinitionCount(); 7 // 通過spring-beans的XSD或DTD實現beanDefinition具體的解析過程 8 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 9 return getRegistry().getBeanDefinitionCount() - countBefore; 10 }該步是註冊bean定義資訊的準備工作,獲取document物件讀取器。
1 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { 2 this.readerContext = readerContext; 3 // 註冊定義在<bean/>標籤的每一個beanDefinition 4 doRegisterBeanDefinitions(doc.getDocumentElement()); 5 }doRegisterBeanDefinitions()是將BeanDefinition物件註冊進容器的核心邏輯,核心虛擬碼如下
1 public static final String PROFILE_ATTRIBUTE = "profile"; 2 public static final String MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; "; 3 4 // 註冊beanDefinitionn的核心處理邏輯 5 protected void doRegisterBeanDefinitions(Element root) { 6 BeanDefinitionParserDelegate parent = this.delegate; 7 // 初始化解析beanDefinition的委託類 8 this.delegate = createDelegate(getReaderContext(), root, parent); 9 // ... 10 // 解析beanDefinition前的處理 11 preProcessXml(root); 12 // 解析beanDefinition資訊 13 parseBeanDefinitions(root, this.delegate); 14 // 解析beanDefinition後的處理 15 postProcessXml(root); 16 17 this.delegate = parent; 18 }doRegisterBeanDefinitions()中的核心步驟 parseBeanDefinitions(root, this.delegate);,preProcessXml和postrocessXml有子類拓展實現,下面來看看DefaultBeanDefinitionDocumentReader中的解析方法parseBeanDefinitions()。
1 // 解析bean定義資訊 2 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 3 // 是否為預設名稱空間 4 if (delegate.isDefaultNamespace(root)) { 5 NodeList nl = root.getChildNodes(); 6 for (int i = 0; i < nl.getLength(); i++) { 7 Node node = nl.item(i); 8 if (node instanceof Element) { 9 Element ele = (Element) node; 10 // 解析預設名稱空間元素,如<bean>標籤 11 if (delegate.isDefaultNamespace(ele)) { 12 parseDefaultElement(ele, delegate); 13 } 14 // 解析自定義名稱空間元素,如<aop>相關標籤 15 else { 16 delegate.parseCustomElement(ele); 17 } 18 } 19 } 20 } 21 else { 22 delegate.parseCustomElement(root); 23 } 24 }parseBeanDefinitions()解析document物件文件樹中的預設標籤、自定義標籤。關於beanDefinition的具體解析,我們放在下一章節介紹。