Spring5.0源碼學習系列之淺談BeanFactory創建 系列文章目錄 博客前言介紹 一、獲取BeanFactory主流程 二、refreshBeanFactory創建過程 知識點歸納

Spring5.0源碼學習系列之淺談BeanFactory創建過程

系列文章目錄

<font color=#999AAA >提示:Spring源碼學習專欄鏈接
<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

@TOC

<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

博客前言介紹

<font color=#999AAA >提示:在上一章的學習中,我們簡單瞭解了Spring IoC容器啓動初始化的主流程,不過並沒有詳細解釋,因爲代碼比較複雜,沒有做長篇大論,所以本文接着學習BenFactory的創建過程,學習源碼建議帶着疑問去學,一點點跟,時間積累之後就可以串起來</font>

<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

<font color=#999AAA >提示:以下是本篇文章正文內容,下面案例可供參考

一、獲取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配置:

 <properties>
   <springframework.version>5.0.9.RELEASE</springframework.version>
 </properties>

 <dependencies>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <version>${springframework.version}</version>
     </dependency>

 </dependencies>

如果是gradle環境,可以直接在Spring框架project裏新增一個module,具體可以參考教程:加上對應的配置


SpringBean.java:

package com.example.bean;

import org.springframework.beans.factory.InitializingBean;

/**
 * <pre>
 *      SpringBean
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改記錄
 *    修改後版本:     修改人:  修改日期: 2020/11/05 10:50  修改內容:
 * </pre>
 */
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 version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="springBean" class="com.example.bean.SpringBean" ></bean>

</beans>

測試類,使用ClassPathXmlApplicationContext

package com.example;

import com.example.bean.SpringBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * <pre>
 *      TestController
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改記錄
 *    修改後版本:     修改人:  修改日期: 2020/11/05 10:22  修改內容:
 * </pre>
 */
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方法



<font color=#999AAA >obtainFreshBeanFactory示例:

/**
 * 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類圖:


從圖可以知道ClassPathXmlApplicationContext通過AbstractRefreshableApplicationContext實現AbstractApplicationContext,所以refreshBeanFactoryAbstractRefreshableApplicationContext#refreshBeanFactory

ok,所以主體的路線,就可以熟悉,用uml時序圖表示:


二、refreshBeanFactory創建過程

refreshBeanFactory方法是BeanFactory的創建過程,接着跟

<font color=#999AAA >org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory代碼如下(示例):

/**
 * 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}


/**
 * 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}


@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}

@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<EncodedResource> 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}.
 * <p>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}

/**
 * This implementation parses bean definitions according to the "spring-beans" XSD
 * (or DTD, historically).
 * <p>Opens a DOM Document; then initializes the default settings
 * specified at the {@code <beans/>} 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 <beans/>} element.
 */
protected void doRegisterBeanDefinitions(Element root) {
    // Any nested <beans> elements will cause recursion in this method. In
    // order to propagate and preserve <beans> 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元素的處理進行講述:

/**
 * 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));
    }
}

<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

知識點歸納

<font color=#999AAA >提示:這裏對文章進行歸納:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章