spring-framework 加載組態檔程序:
根據業務系統運行的環境,選擇 ApplicationContext 介面合適的實作類,啟動系統先初始化背景關系環境,然后通過BeanDefinitionReader 的實作類讀取Bean的組態檔,這里組態檔可以是 xml檔案,properties檔案,yml檔案等,也可以是注解形式掃描配置,組態檔讀取成功后,將相應的配置轉換成 BeanDefinition 的物件實體保存在DefaultListableBeanFactory#beanDefinitionMap 中,緊接著,根據配置的 BeanFactoryPostProcessor 物件串列,進行 BeanDefinition 的擴展處理,( 例如 org.springframework.beans.factory.config.PlaceholderConfigurerSupport: ${} 配置轉換成實際的值實作類,org.springframework.beans.factory.config.PropertyOverrideConfigurer: 將 beanName.property 替換成實際的值, )
--> xml配置
|
ApplicationContext ---> BeanDefinitionReader -+----> yml配置 -----> BeanDefinition ---> DefaultListableBeanFactory ---> BeanFactoryPostProcessor
|
--> Annotation 掃描
這里以 XML 配置的BeanDefinition 進行原始碼加載分析:
一. BeanFactory 實體的創建
在 AbstractApplicationContext#obtainFreshBeanFactory 方法中,是進行 bean 處理的入口,
AbstractApplicationContext#obtainFreshBeanFactory
-> AbstractRefreshableApplicationContext#refreshBeanFactory
1.檢測是否已經創建 BeanFactory ,如果創建了則銷毀BeanFactory 相關的屬性,
2.創建 BeanFactory 介面的默認實作類 DefaultListableBeanFactory 實體,
3.客戶化 BeanFactory 處理,
4.加載 BeanDefinition 定義資訊,
1 /** 2 * This implementation performs an actual refresh of this context's underlying 3 * bean factory, shutting down the previous bean factory (if any) and 4 * initializing a fresh bean factory for the next phase of the context's lifecycle. 5 */ 6 @Override 7 protected final void refreshBeanFactory() throws BeansException { 8 if (hasBeanFactory()) { 9 destroyBeans(); 10 closeBeanFactory(); 11 } 12 try { 13 DefaultListableBeanFactory beanFactory = createBeanFactory(); 14 beanFactory.setSerializationId(getId()); 15 customizeBeanFactory(beanFactory); 16 loadBeanDefinitions(beanFactory); 17 this.beanFactory = beanFactory; 18 } 19 catch (IOException ex) { 20 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); 21 } 22 }
二. ApplicationContex 讀取 BeanDefinition 的加載分析
這里我們分析 XML 形式配置的加載,因此通過 AbstractXmlApplicationContext 來分析原始碼,
AbstractApplicationContext#obtainFreshBeanFactory
-> AbstractRefreshableApplicationContext#refreshBeanFactory
--> AbstractXmlApplicationContext#loadBeanDefinitions(DefaultListableBeanFactory)
1.首先創建讀取 BeanDefinition 的 BeanDefinitionReader 介面實作 XmlBeanDefinitionReader 實體,并設定環境資訊,
2.通過 BeanDefinitionReader 實作讀取 BeanDefinition 配置資訊,
1 /** 2 * Loads the bean definitions via an XmlBeanDefinitionReader. 3 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader 4 * @see #initBeanDefinitionReader 5 * @see #loadBeanDefinitions 6 */ 7 @Override 8 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { 9 // Create a new XmlBeanDefinitionReader for the given BeanFactory. 10 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); 11 12 // Configure the bean definition reader with this context's 13 // resource loading environment. 14 beanDefinitionReader.setEnvironment(this.getEnvironment()); 15 beanDefinitionReader.setResourceLoader(this); 16 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); 17 18 // Allow a subclass to provide custom initialization of the reader, 19 // then proceed with actually loading the bean definitions. 20 initBeanDefinitionReader(beanDefinitionReader); 21 loadBeanDefinitions(beanDefinitionReader); 22 }
三. BeanDefinitionReader 讀取,加載 BeanDefinition 配置分析
AbstractXmlApplicationContext#loadBeanDefinitions(DefaultListableBeanFactory)
--> AbstractXmlApplicationContext#loadBeanDefinitions(XmlBeanDefinitionReader)
--> AbstractBeanDefinitionReader#loadBeanDefinitions(String, Set<Resource>)
--> XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource)
--> XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource, Resource )
1.讀取 XML 檔案,獲取到 Document 實體,為后續決議配置做準備,
2.向 BeanFactory 中注冊 BeanDefinition 配置資訊,
此處例外捕獲比較多,學習例外規范處理,最底層的 Throwable 例外也捕獲了,保證程式不會例外退出,
1 /** 2 * Actually load bean definitions from the specified XML file. 3 * @param inputSource the SAX InputSource to read from 4 * @param resource the resource descriptor for the XML file 5 * @return the number of bean definitions found 6 * @throws BeanDefinitionStoreException in case of loading or parsing errors 7 * @see #doLoadDocument 8 * @see #registerBeanDefinitions 9 */ 10 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) 11 throws BeanDefinitionStoreException { 12 13 try { 14 Document doc = doLoadDocument(inputSource, resource); 15 int count = registerBeanDefinitions(doc, resource); 16 if (logger.isDebugEnabled()) { 17 logger.debug("Loaded " + count + " bean definitions from " + resource); 18 } 19 return count; 20 } 21 catch (BeanDefinitionStoreException ex) { 22 throw ex; 23 } 24 catch (SAXParseException ex) { 25 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 26 "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); 27 } 28 catch (SAXException ex) { 29 throw new XmlBeanDefinitionStoreException(resource.getDescription(), 30 "XML document from " + resource + " is invalid", ex); 31 } 32 catch (ParserConfigurationException ex) { 33 throw new BeanDefinitionStoreException(resource.getDescription(), 34 "Parser configuration exception parsing XML from " + resource, ex); 35 } 36 catch (IOException ex) { 37 throw new BeanDefinitionStoreException(resource.getDescription(), 38 "IOException parsing XML document from " + resource, ex); 39 } 40 catch (Throwable ex) { 41 throw new BeanDefinitionStoreException(resource.getDescription(), 42 "Unexpected exception parsing XML document from " + resource, ex); 43 } 44 }
四.BeanDefinitionDocumentReader 決議 BeanDefinition 配置分析
(一) BeanDefinitionDocumentReader 分析
XmlBeanDefinitionReader#doLoadBeanDefinitions
--> XmlBeanDefinitionReader#registerBeanDefinitions(Document, Resource)
--> BeanDefinitionDocumentReader#registerBeanDefinitionsregisterBeanDefinitions(Document, XmlReaderContext)
1. 創建 BeanDefinitionDocumentReader 物件實體,
2.創建 讀取 BeanDefinition 配置資訊Document的背景關系(XmlReaderContext),為后續決議配置提供相關支持,
3. 決議并注冊Document 中配置的 BeanDefinition 資訊,在 DefaultBeanDefinitionDocumentReader 實作具體邏輯,
1 /** 2 * Register the bean definitions contained in the given DOM document. 3 * Called by {@code loadBeanDefinitions}. 4 * <p>Creates a new instance of the parser class and invokes 5 * {@code registerBeanDefinitions} on it. 6 * @param doc the DOM document 7 * @param resource the resource descriptor (for context information) 8 * @return the number of bean definitions found 9 * @throws BeanDefinitionStoreException in case of parsing errors 10 * @see #loadBeanDefinitions 11 * @see #setDocumentReaderClass 12 * @see BeanDefinitionDocumentReader#registerBeanDefinitions 13 */ 14 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { 15 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); 16 int countBefore = getRegistry().getBeanDefinitionCount(); 17 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 18 return getRegistry().getBeanDefinitionCount() - countBefore; 19 }
(二)BeanDefinitionDocumentReader 實作類 DefaultBeanDefinitionDocumentReader分析
BeanDefinitionDocumentReader#registerBeanDefinitionsregisterBeanDefinitions(Document, XmlReaderContext)
--> DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
--> DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element)
1. 根據 ReaderContext 資訊,創建對應的 實體,
2. 檢查 profile 配置資訊,如果不匹配,則不決議當前root節點下的 BeanDefinition 配置,
3. 當前root節點 前置處理,
4. 決議當前 root 節點的 BeanDefinition 配置資訊,
5. 當前root節點 后置處理,
(三) DefaultBeanDefinitionDocumentReader#parseBeanDefinitions 分析
DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
--> DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
--> BeanDefinitionParserDelegate#parseCustomElement(Element)
1. 判斷 root 節點 DefaultNamespace 是否是 http://www.springframework.org/schema/beans ,
如果是,則遍歷子節點進行決議,
當子節點 是 DefaultNamespace ,則使用 DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 決議節點,
否做使用 BeanDefinitionParserDelegate#parseCustomElement(Element) 決議節點,
2. 非默認 DefaultNamespace 值,則使用 BeanDefinitionParserDelegate#parseCustomElement(Element) 決議節點,
例如 AOP 配置節點決議,例如:<aop:config>
事物配置節點決議,例如:<tx:annotation-driven proxy-target- />
(四) 決議 DefaultNamespace 節點 BeanDefinition 資訊,
DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
--> DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource(Element)
--> DefaultBeanDefinitionDocumentReader#processAliasRegistration(Element)
--> DefaultBeanDefinitionDocumentReader#processBeanDefinition(Element, BeanDefinitionParserDelegate)
--> DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element)
1.依次決議 import 節點,alias 節點,bean節點, beans節點 定義的 BeanDefinition 資訊,
(五) 決議 非默認命名空間節點 BeanDefinition 資訊,
BeanDefinitionParserDelegate#parseCustomElement(Element)
--> BeanDefinitionParserDelegate#parseCustomElement(Element, BeanDefinition)
1.獲取節點命名空間,
2.根據命名空間獲取對應的決議器,
3. 決議節點 BeanDefinition 資訊,
1 /** 2 * Parse a custom element (outside of the default namespace). 3 * @param ele the element to parse 4 * @param containingBd the containing bean definition (if any) 5 * @return the resulting bean definition 6 */ 7 @Nullable 8 public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { 9 String namespaceUri = getNamespaceURI(ele); 10 if (namespaceUri == null) { 11 return null; 12 } 13 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 14 if (handler == null) { 15 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); 16 return null; 17 } 18 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); 19 }
五.BeanDefinitionDocumentReader 決議 bean 節點
XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource)
--> XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource, Resource)
--> XmlBeanDefinitionReader#registerBeanDefinitions(Document, Resource)
--> DefaultBeanDefinitionDocumentReader#registerBeanDefinitions(Document, XmlReaderContext)
--> DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions(Element)
--> DefaultBeanDefinitionDocumentReader#parseBeanDefinitions(Element , BeanDefinitionParserDelegate)
--> DefaultBeanDefinitionDocumentReader#parseDefaultElement(Element, BeanDefinitionParserDelegate)
--> DefaultBeanDefinitionDocumentReader#processBeanDefinition(Element, BeanDefinitionParserDelegate)
1.使用 BeanDefinitionParserDelegate 決議 Bean 配置節點,得到保存 BeanDefinition 資訊的輔助物件 BeanDefinitionHolder 實體,
2.當 決議的 Bean 配置 BeanDefinitionHolder 實體不為空,則進行 Bean 配置 后續處理,
a. 當 Bean 配置節點 找到 其他 Namespace 對應 處理器(NamespaceHandler),則進行特殊化處理,
b. 向 BeanFactory 中注冊決議成功的 BeanDefinition 定義,
c.發送 BeanDefinition 決議成功事件 BeanComponentDefinition,
1 /** 2 * Process the given bean element, parsing the bean definition 3 * and registering it with the registry. 4 */ 5 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { 6 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 7 if (bdHolder != null) { 8 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 9 try { 10 // Register the final decorated instance. 11 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); 12 } 13 catch (BeanDefinitionStoreException ex) { 14 getReaderContext().error("Failed to register bean definition with name '" + 15 bdHolder.getBeanName() + "'", ele, ex); 16 } 17 // Send registration event. 18 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 19 } 20 }
六. BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition) 決議 bean 節點
DefaultBeanDefinitionDocumentReader#processBeanDefinition(Element, BeanDefinitionParserDelegate)
--> BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element)
--> BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element, BeanDefinition)
1.決議 Element 元素的 id 和 name 屬性,得到 beanName 和 aliases,
2.檢查 beanName 是否唯一,
3. 創建 AbstractBeanDefinition 物件實體 bd ,并通過依次決議 Element 元素的屬性給 bd 賦值,
依次決議子節點 meta, lookup-method, replaced-method, constructor-arg, property, qualifier,并將決議后的值添加到 bd 相關變數,
設定 bd 解析資源的相關資訊,
4. 如果 3 決議失敗,則回傳的 實體BeanDefinition 為 null,則這里也直接回傳,
5. 當上述步驟決議的 beanName 為 空時,使用指定方式生成 beanName,
當前 bean 是在其他Bean 內部時, 使用BeanDefinitionReaderUtils#generateBeanName(BeanDefinition, BeanDefinitionRegistry, boolean) 生成 beanName,
否則使用 XmlReaderContext 對應生成器生成 beanName,
6.根據決議的 BeanDefinition 創建對應的 BeanDefinitionHolder 物件實體,
在決議 bean 配置資訊時,主要有如下 七個元資料包裝類,對相關屬性配置進行歸檔:
org.springframework.beans.factory.config.ConstructorArgumentValues <constructor-arg>
org.springframework.beans.PropertyValue <property>
org.springframework.beans.factory.support.ManagedArray <array>
org.springframework.beans.factory.support.ManagedList <list>
org.springframework.beans.factory.support.ManagedSet <set>
org.springframework.beans.factory.support.ManagedMap <map>
org.springframework.beans.factory.support.ManagedProperties <props>
每個配置具體字串值對應了如下三種 處理型別:
org.springframework.beans.factory.config.TypedStringValue <value>
org.springframework.beans.factory.config.RuntimeBeanReference <ref>
org.springframework.beans.factory.config.RuntimeBeanNameReference <idref>
1 /** 2 * Parses the supplied {@code <bean>} element. May return {@code null} 3 * if there were errors during parse. Errors are reported to the 4 * {@link org.springframework.beans.factory.parsing.ProblemReporter}. 5 */ 6 @Nullable 7 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { 8 String id = ele.getAttribute(ID_ATTRIBUTE); 9 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); 10 11 List<String> aliases = new ArrayList<>(); 12 if (StringUtils.hasLength(nameAttr)) { 13 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); 14 aliases.addAll(Arrays.asList(nameArr)); 15 } 16 17 String beanName = id; 18 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { 19 beanName = aliases.remove(0); 20 if (logger.isTraceEnabled()) { 21 logger.trace("No XML 'id' specified - using '" + beanName + 22 "' as bean name and " + aliases + " as aliases"); 23 } 24 } 25 26 if (containingBean == null) { 27 checkNameUniqueness(beanName, aliases, ele); 28 } 29 30 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); 31 if (beanDefinition != null) { 32 if (!StringUtils.hasText(beanName)) { 33 try { 34 if (containingBean != null) { 35 beanName = BeanDefinitionReaderUtils.generateBeanName( 36 beanDefinition, this.readerContext.getRegistry(), true); 37 } 38 else { 39 beanName = this.readerContext.generateBeanName(beanDefinition); 40 // Register an alias for the plain bean class name, if still possible, 41 // if the generator returned the class name plus a suffix. 42 // This is expected for Spring 1.2/2.0 backwards compatibility. 43 String beanClassName = beanDefinition.getBeanClassName(); 44 if (beanClassName != null && 45 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && 46 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { 47 aliases.add(beanClassName); 48 } 49 } 50 if (logger.isTraceEnabled()) { 51 logger.trace("Neither XML 'id' nor 'name' specified - " + 52 "using generated bean name [" + beanName + "]"); 53 } 54 } 55 catch (Exception ex) { 56 error(ex.getMessage(), ele); 57 return null; 58 } 59 } 60 String[] aliasesArray = StringUtils.toStringArray(aliases); 61 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); 62 } 63 64 return null; 65 }View Code
七. BeanDefinitionParserDelegate#decorateIfRequired(Node, BeanDefinitionHolder, @Nullable BeanDefinition)決議 bean 節點自定義屬性
DefaultBeanDefinitionDocumentReader#processBeanDefinition(Element, BeanDefinitionParserDelegate)
--> BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element)
--> BeanDefinitionParserDelegate#decorateBeanDefinitionIfRequired(Element, BeanDefinitionHolder)
--> BeanDefinitionParserDelegate#decorateBeanDefinitionIfRequired(Element, BeanDefinitionHolder, BeanDefinition)
--> BeanDefinitionParserDelegate#decorateIfRequired(Node, BeanDefinitionHolder, @Nullable BeanDefinition)
1. 獲得節點(Node) 的命名空間URI(NamespaceURI),
2. 如果命名空間URI不是默認的,則進行特殊處理,
3. 通過 命名空間URI 到讀取檔案背景關系(XmlReaderContext) 中查找對應 處理器(NamespaceHandler),
例如
Context的處理器: org.springframework.context.config.ContextNamespaceHandler
AOP的處理器: org.springframework.aop.config.AopNamespaceHandler
component的處理器: org.springframework.beans.factory.xml.ComponentNamespaceHandler
4. 呼叫 NamespaceHandler#decorate(Node, BeanDefinitionHolder, ParserContext)方法決議 節點配置資訊,并回傳新的 BeanDefinitionHolder 實體,
1 /** 2 * Decorate the given bean definition through a namespace handler, 3 * if applicable. 4 * @param node the current child node 5 * @param originalDef the current bean definition 6 * @param containingBd the containing bean definition (if any) 7 * @return the decorated bean definition 8 */ 9 public BeanDefinitionHolder decorateIfRequired( 10 Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { 11 12 String namespaceUri = getNamespaceURI(node); 13 if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) { 14 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 15 if (handler != null) { 16 BeanDefinitionHolder decorated = 17 handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd)); 18 if (decorated != null) { 19 return decorated; 20 } 21 } 22 else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) { 23 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node); 24 } 25 else { 26 // A custom namespace, not to be handled by Spring - maybe "xml:...". 27 if (logger.isDebugEnabled()) { 28 logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]"); 29 } 30 } 31 } 32 return originalDef; 33 }View Code
八. BeanDefinitionReaderUtils#registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry) 向 BeanFactory 注冊 BeanDefinition 實體資訊
1. 通過 beanName 向 beanFactory 中注冊 BeanDefinition 實體,
2. 通過 beanName 向 beanFactory 中注冊 BeanDefinition 實體的別名串列,
1 /** 2 * Register the given bean definition with the given bean factory. 3 * @param definitionHolder the bean definition including name and aliases 4 * @param registry the bean factory to register with 5 * @throws BeanDefinitionStoreException if registration failed 6 */ 7 public static void registerBeanDefinition( 8 BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 9 throws BeanDefinitionStoreException { 10 11 // Register bean definition under primary name. 12 String beanName = definitionHolder.getBeanName(); 13 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); 14 15 // Register aliases for bean name, if any. 16 String[] aliases = definitionHolder.getAliases(); 17 if (aliases != null) { 18 for (String alias : aliases) { 19 registry.registerAlias(beanName, alias); 20 } 21 } 22 }View Code
-----------------------------------------------------作者:流羽 經流年 看繁花似錦 望星辰不變 出處:https://www.cnblogs.com/StreamPlume/p/15673202.html
如果覺得這篇文章對你有小小的幫助的話,記得在右下角點個“推薦”哦,博主在此感謝!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/386463.html
標籤:其他
上一篇:1.6 字典中的鍵映射多個值
下一篇:1.7 字典排序
