1. 程式人生 > >Spring5.0原始碼學習系列之淺談BeanFactory建立

Spring5.0原始碼學習系列之淺談BeanFactory建立

Spring5.0原始碼學習系列之淺談BeanFactory建立過程 # 系列文章目錄 提示:[Spring原始碼學習專欄連結](https://blog.csdn.net/u014427391/category_10493299.html)
@[TOC](文章目錄)
# 部落格前言介紹 提示:在[上一章](https://smilenicky.blog.csdn.net/article/details/109118283)的學習中,我們簡單瞭解了Spring IoC容器啟動初始化的主流程,不過並沒有詳細解釋,因為程式碼比較複雜,沒有做長篇大論,所以本文接著學習BenFactory的建立過程,學習原始碼建議帶著疑問去學,一點點跟,時間積累之後就可以串起來
提示:以下是本篇文章正文內容,下面案例可供參考 # 一、獲取BeanFactory主流程 在前面的學習中,#refresh是IoC容器建立很關鍵的主線,在程式碼裡,可以找到`obtainFreshBeanFactory`這個比較關鍵的方法,這個方法就是BeanFactory的建立過程方法,本文`ClassPathXmlApplicationContext`為準,進行原始碼的學習,上篇部落格是以`AnnotationApplicationContext`為準進行簡單的分析,本文繼續比較詳細的講述 實驗環境: * SpringFramework版本 * Springframework5.0.x * 開發環境 * JAR管理:gradle 4.9/ Maven3.+ * 開發IDE:IntelliJ IDEA 2018.2.5 * JDK:jdk1.8.0_31 * Git Server:Git fro window 2.8.3 * Git Client:SmartGit18.1.5(可選) maven專案,需要加上pom配置: ```xml 5.0.9.RELEASE
org.springframework spring-context ${springframework.version} ``` 如果是gradle環境,可以直接在Spring框架project裡新增一個module,具體可以參考[教程](https://smilenicky.blog.csdn.net/article/details/109293367):加上對應的配置 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201109173423963.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0MjczOTE=,size_16,color_FFFFFF,t_70#pic_center) SpringBean.java: ```java package com.example.bean; import org.springframework.beans.factory.InitializingBean; /** *
 *      SpringBean
 * 
* *
 * @author mazq
 * 修改記錄
 *    修改後版本:     修改人:  修改日期: 2020/11/05 10:50  修改內容:
 * 
*/ public class SpringBean implements InitializingBean { public SpringBean(){ System.out.println("SpringBean建構函式"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("SpringBean afterPropertiesSet"); } } ``` applicationContext.xml: ```xml
``` 測試類,使用ClassPathXmlApplicationContext ```java package com.example; import com.example.bean.SpringBean; import org.springframework.context.support.ClassPathXmlApplicationContext; /** *
 *      TestController
 * 
* *
 * @author mazq
 * 修改記錄
 *    修改後版本:     修改人:  修改日期: 2020/11/05 10:22  修改內容:
 * 
*/ public class TestApplication { public static void testClassPathXmlApplicationContext() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); SpringBean springBean = context.getBean(SpringBean.class); System.out.println(springBean); } public static void main(String[] args) { // 測試ClassPathXmlApplicationContext testClassPathXmlApplicationContext(); } } ``` 在SpringBean 打斷點,debug進行除錯:找到#refresh方法 ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2020111114263066.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0MjczOTE=,size_16,color_FFFFFF,t_70#pic_center) obtainFreshBeanFactory示例: ```java /** * Tell the subclass to refresh the internal bean factory. * @return the fresh BeanFactory instance * @see #refreshBeanFactory() * @see #getBeanFactory() */ protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 建立BeanFactory refreshBeanFactory(); // 返回refreshBeanFactory方法建立好的BeanFactory ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; } ``` 然後`refreshBeanFactory`是由哪個基類實現的?我們找來一張uml類圖: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201111142257736.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0MjczOTE=,size_16,color_FFFFFF,t_70#pic_center) 從圖可以知道`ClassPathXmlApplicationContext`通過`AbstractRefreshableApplicationContext`實現`AbstractApplicationContext`,所以`refreshBeanFactory`是`AbstractRefreshableApplicationContext#refreshBeanFactory` ok,所以主體的路線,就可以熟悉,用uml時序圖表示: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201111150952587.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0MjczOTE=,size_16,color_FFFFFF,t_70#pic_center) # 二、refreshBeanFactory建立過程 `refreshBeanFactory`方法是BeanFactory的建立過程,接著跟 `org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory`程式碼如下(示例): ```java /** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. */ @Override protected final void refreshBeanFactory() throws BeansException { // 先判斷是否有BeanFactory if (hasBeanFactory()) { // destroy Beans destroyBeans(); // 關閉BeanFactory closeBeanFactory(); } try { // 例項化DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); // 設定序列化id beanFactory.setSerializationId(getId()); // 定義BeanFactory的一些屬性(是否允許Bean覆蓋,是否允許迴圈依賴) customizeBeanFactory(beanFactory); // 載入應用中的BeanDefinition loadBeanDefinitions(beanFactory); this.beanFactory = beanFactory; } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } } ``` `{@link org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions}` ```java /** * Loads the bean definitions via an XmlBeanDefinitionReader. * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see #initBeanDefinitionReader * @see #loadBeanDefinitions */ @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. // 給BeanFactory例項化一個XmlBeanDefinitionReader例項 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. // 給子類提供初始化XmlBeanDefinitionReader的模板方法 initBeanDefinitionReader(beanDefinitionReader); // 重點在這,繼續跟 loadBeanDefinitions(beanDefinitionReader); } // org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory) protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { // 繼續跟 reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } } ``` `{@link org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions}` ```java @Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; // 變數資源物件,載入BeanDefinition for (Resource resource : resources) { // 在這裡,經過debug,調到XmlBeanDefinitionReader.loadBeanDefinitions(resource) counter += loadBeanDefinitions(resource); } // 返回統計數量,表示總共有多少個BeanDefinition return counter; } ``` `XmlBeanDefinitionReader`類 `{@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions}` ```java @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } // 省略... /** * Load bean definitions from the specified XML file. * @param encodedResource the resource descriptor for the XML file, * allowing to specify an encoding to use for parsing the file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ 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); } // 用ThreadLocal存放資源物件,目的是保證執行緒安全 Set currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic 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.remove(); } } } /** * Actually load bean definitions from the specified XML file. * @param inputSource the SAX InputSource to read from * @param resource the resource descriptor for the XML file * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors * @see #doLoadDocument * @see #registerBeanDefinitions */ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // 讀取xml資訊,將xml資訊儲存在Document物件 Document doc = doLoadDocument(inputSource, resource); // 封裝BeanDefinitions並註冊,繼續解析Document物件,往下跟 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); } } /** * Register the bean definitions contained in the given DOM document. * Called by {@code loadBeanDefinitions}. *

Creates a new instance of the parser class and invokes * {@code registerBeanDefinitions} on it. * @param doc the DOM document * @param resource the resource descriptor (for context information) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of parsing errors * @see #loadBeanDefinitions * @see #setDocumentReaderClass * @see BeanDefinitionDocumentReader#registerBeanDefinitions */ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 獲取已有的BeanDefinition數量 int countBefore = getRegistry().getBeanDefinitionCount(); // 註冊BeanDefinition,關注點:registerBeanDefinitions、createReaderContext documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 返回新註冊的BeanDefinition統計數量 return getRegistry().getBeanDefinitionCount() - countBefore; } ``` `{@link org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions}` ```java /** * This implementation parses bean definitions according to the "spring-beans" XSD * (or DTD, historically). *

Opens a DOM Document; then initializes the default settings * specified at the {@code } level; then parses the contained bean definitions. */ @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); // 往下跟,從xml根節點開始解析檔案 doRegisterBeanDefinitions(root); } /** * Register each bean definition within the given root {@code } element. */ protected void doRegisterBeanDefinitions(Element root) { // Any nested elements will cause recursion in this method. In // order to propagate and preserve default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { // profile(環境校驗) String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // 不是當前profile的,跳過 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } // preProcessXml模板方法 preProcessXml(root); // 重點是parseBeanDefinitions parseBeanDefinitions(root, this.delegate); // postProcessXml 同樣是模板方法 過 postProcessXml(root); this.delegate = parent; } /** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { // 解析default namespace下面的元素 parseDefaultElement(ele, delegate); } else { // 解析自定義標籤元素 delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } } private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { // import元素處理 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } // alias元素處理 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } // bean元素處理 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } // 巢狀beans處理 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } } ``` bean元素處理,本文挑beana元素的處理進行講述: ```java /** * Process the given bean element, parsing the bean definition * and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //解析Bean元素為BeanDefinition,再通過BeanDefinitionHolder封裝成BeanDefinitionHolder物件 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { // 有自定義屬性會進行相應的解析 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. // BeanDefinition的註冊過程 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)); } } ```


# 知識點歸納 提示:這裡對文章進行歸納: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20201109160646412.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0MjczOTE=,size_16,color_FFFFFF,t_70#pic