Spring框架系列(7) - Spring IOC實現原理詳解之IOC初始化流程

語言: CN / TW / HK

上文,我們看了IOC設計要點和設計結構;緊接著這篇,我們可以看下原始碼的實現了:Spring如何實現將資源配置(以xml配置為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的。@pdai

  • Spring框架系列(7) - Spring IOC實現原理詳解之IOC初始化流程

引入

上文,我們看了IOC設計要點和設計結構;緊接著這篇,我們可以看下原始碼的實現了:Spring如何實現將資源配置(以xml配置為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的(就是我們圈出來的部分)

如何將Bean從XML配置中解析後放到IoC容器中的?

本文的目標就是分析Spring如何實現將資源配置(以xml配置為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的。

初始化的入口

對於xml配置的Spring應用,在main()方法中例項化ClasspathXmlApplicationContext即可建立一個IoC容器。我們可以從這個構造方法開始,探究一下IoC容器的初始化過程。

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("aspects.xml", "daos.xml", "services.xml");
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
    this(configLocations, true, (ApplicationContext)null);
}

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
    // 設定Bean資源載入器
    super(parent);

    // 設定配置路徑
    this.setConfigLocations(configLocations);

    // 初始化容器
    if (refresh) {
        this.refresh();
    }
}

設定資源解析器和環境

呼叫父類容器AbstractApplicationContext的構造方法( super(parent) 方法)為容器設定好Bean資源載入器

public AbstractApplicationContext(@Nullable ApplicationContext parent) {
    // 預設建構函式初始化容器id, name, 狀態 以及 資源解析器
    this();

    // 將父容器的Environment合併到當前容器
    this.setParent(parent);
}

通過AbstractApplicationContext預設建構函式初始化容器id, name, 狀態 以及 資源解析器

public AbstractApplicationContext() {
    this.logger = LogFactory.getLog(this.getClass());
    this.id = ObjectUtils.identityToString(this);
    this.displayName = ObjectUtils.identityToString(this);
    this.beanFactoryPostProcessors = new ArrayList();
    this.active = new AtomicBoolean();
    this.closed = new AtomicBoolean();
    this.startupShutdownMonitor = new Object();
    this.applicationStartup = ApplicationStartup.DEFAULT;
    this.applicationListeners = new LinkedHashSet();
    this.resourcePatternResolver = this.getResourcePatternResolver();
}
// Spring資源載入器
protected ResourcePatternResolver getResourcePatternResolver() {
    return new PathMatchingResourcePatternResolver(this);
}

通過AbstractApplicationContext的 setParent(parent) 方法將父容器的Environment合併到當前容器

public void setParent(@Nullable ApplicationContext parent) {
    this.parent = parent;
    if (parent != null) {
        Environment parentEnvironment = parent.getEnvironment();
        if (parentEnvironment instanceof ConfigurableEnvironment) {
            this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
        }
    }
}

設定配置路徑

在設定容器的資源載入器之後,接下來FileSystemXmlApplicationContet執行setConfigLocations方法通過呼叫其父類AbstractRefreshableConfigApplicationContext的方法進行對Bean定義資原始檔的定位

public void setConfigLocations(@Nullable String... locations) {
    if (locations != null) {
        Assert.noNullElements(locations, "Config locations must not be null");
        this.configLocations = new String[locations.length];

        for(int i = 0; i < locations.length; ++i) {
            // 解析配置路徑
            this.configLocations[i] = this.resolvePath(locations[i]).trim();
        }
    } else {
        this.configLocations = null;
    }
}
protected String resolvePath(String path) {
    // 從上一步Environment中解析
    return this.getEnvironment().resolveRequiredPlaceholders(path);
}

初始化的主體流程

Spring IoC容器對Bean定義資源的載入是從refresh()函式開始的,refresh()是一個模板方法,refresh()方法的作用是:在建立IoC容器前,如果已經有容器存在,則需要把已有的容器銷燬和關閉,以保證在refresh之後使用的是新建立起來的IoC容器。refresh的作用類似於對IoC容器的重啟,在新建立好的容器中對容器進行初始化,對Bean定義資源進行載入。

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}

這裡的設計上是一個非常典型的資源類載入處理型的思路,頭腦中需要形成如下圖的 頂層思路 (而不是隻停留在流水式的方法上面):

  • 模板方法設計模式 ,模板方法中使用典型的 鉤子方法
  • 具體的初始化載入方法 插入到鉤子方法之間
  • 將初始化的階段封裝,用來記錄當前初始化到什麼階段;常見的設計是xxxPhase/xxxStage;
  • 資源載入初始化有失敗等處理,必然是 try/catch/finally ...

初始化BeanFactory之obtainFreshBeanFactory

AbstractApplicationContext的obtainFreshBeanFactory()方法呼叫子類容器的refreshBeanFactory()方法,啟動容器載入Bean定義資原始檔的過程,程式碼如下:

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 這裡使用了委派設計模式,父類定義了抽象的refreshBeanFactory()方法,具體實現呼叫子類容器的refreshBeanFactory()方法
    refreshBeanFactory();
    return getBeanFactory();
}

AbstractApplicationContext類中只抽象定義了refreshBeanFactory()方法,容器真正呼叫的是其子類AbstractRefreshableApplicationContext實現的refreshBeanFactory()方法;

在建立IoC容器前,如果已經有容器存在,則需要把已有的容器銷燬和關閉,以保證在refresh之後使用的是新建立起來的IoC容器。方法的原始碼如下:

protected final void refreshBeanFactory() throws BeansException {
    // 如果已經有容器存在,則需要把已有的容器銷燬和關閉,以保證在refresh之後使用的是新建立起來的IoC容器
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 建立DefaultListableBeanFactory,並呼叫loadBeanDefinitions(beanFactory)裝載bean定義
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory); // 對IoC容器進行定製化,如設定啟動引數,開啟註解的自動裝配等 
        loadBeanDefinitions(beanFactory); // 呼叫載入Bean定義的方法,主要這裡又使用了一個委派模式,在當前類中只定義了抽象的loadBeanDefinitions方法,具體的實現呼叫子類容器  
        this.beanFactory = beanFactory;
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

初始化BeanFactory之loadBeanDefinitions

AbstractRefreshableApplicationContext中只定義了抽象的loadBeanDefinitions方法,容器真正呼叫的是其子類AbstractXmlApplicationContext對該方法的實現,AbstractXmlApplicationContext的主要原始碼如下:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 建立XmlBeanDefinitionReader,即建立Bean讀取器,並通過回撥設定到容器中去,容器使用該讀取器讀取Bean定義資源  
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // 配置上下文的環境,資源載入器、解析器
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 為Bean讀取器設定SAX xml解析器

    // 允許子類自行初始化(比如校驗機制),並提供真正的載入方法
    initBeanDefinitionReader(beanDefinitionReader); // 當Bean讀取器讀取Bean定義的Xml資原始檔時,啟用Xml的校驗機制  
    loadBeanDefinitions(beanDefinitionReader);
}

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    // 載入XML配置方式裡的Bean定義的資源
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
        reader.loadBeanDefinitions(configResources);
    }
    // 載入建構函式裡配置的Bean配置檔案,即{"aspects.xml", "daos.xml", "services.xml"}
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        reader.loadBeanDefinitions(configLocations);
    }
}

Xml Bean讀取器(XmlBeanDefinitionReader)呼叫其父類AbstractBeanDefinitionReader的 reader.loadBeanDefinitions方法讀取Bean定義資源。

由於我們使用ClassPathXmlApplicationContext作為例子分析,因此getConfigResources的返回值為null,因此程式執行reader.loadBeanDefinitions(configLocations)分支。

AbstractBeanDefinitionReader讀取Bean定義資源

AbstractBeanDefinitionReader的loadBeanDefinitions方法原始碼如下:

@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(location, null);
}

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    ResourceLoader resourceLoader = getResourceLoader();
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException(
                "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
    }

    // 模式匹配型別的解析器,這種方式是載入多個滿足匹配條件的資源
    if (resourceLoader instanceof ResourcePatternResolver) {
        try {
            // 獲取到要載入的資源
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            int count = loadBeanDefinitions(resources); // 委派呼叫其子類XmlBeanDefinitionReader的方法,實現載入功能  
            if (actualResources != null) {
                Collections.addAll(actualResources, resources);
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
            }
            return count;
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "Could not resolve bean definition resource pattern [" + location + "]", ex);
        }
    }
    else {
        // 只能通過絕對路徑URL載入單個資源.
        Resource resource = resourceLoader.getResource(location);
        int count = loadBeanDefinitions(resource);
        if (actualResources != null) {
            actualResources.add(resource);
        }
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
        }
        return count;
    }
}

從對AbstractBeanDefinitionReader的loadBeanDefinitions方法原始碼分析可以看出該方法做了以下兩件事:

  • 首先,呼叫資源載入器的獲取資源方法resourceLoader.getResource(location),獲取到要載入的資源。
  • 其次,真正執行載入功能是其子類XmlBeanDefinitionReader的loadBeanDefinitions方法。

XmlBeanDefinitionReader載入Bean定義資源

繼續看子類XmlBeanDefinitionReader的loadBeanDefinitions(Resource …)方法看到代表bean檔案的資源定義以後的載入過程。

/**
    * 本質上是載入XML配置的Bean。
    * @param inputSource the SAX InputSource to read from
    * @param resource the resource descriptor for the XML file
    */
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {

    try {
        Document doc = doLoadDocument(inputSource, resource); // 將Bean定義資源轉換成Document物件
        int count = registerBeanDefinitions(doc, resource);
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        return count;
    }
    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);
    }
}

// 使用配置的DocumentLoader載入XML定義檔案為Document.
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
            getValidationModeForResource(resource), isNamespaceAware());
}

通過原始碼分析,載入Bean定義資原始檔的最後一步是將Bean定義資源轉換為Document物件,該過程由documentLoader實現

DocumentLoader將Bean定義資源轉換為Document物件

DocumentLoader將Bean定義資源轉換成Document物件的原始碼如下:

// 使用標準的JAXP將載入的Bean定義資源轉換成document物件
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
        ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

    // 建立檔案解析器工廠
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isTraceEnabled()) {
        logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    // 建立文件解析器
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    return builder.parse(inputSource); // 解析
}

protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
        throws ParserConfigurationException {

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(namespaceAware);

    // 設定解析XML的校驗
    if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
        factory.setValidating(true);
        if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
            // Enforce namespace aware for XSD...
            factory.setNamespaceAware(true);
            try {
                factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
            }
            catch (IllegalArgumentException ex) {
                ParserConfigurationException pcex = new ParserConfigurationException(
                        "Unable to validate using XSD: Your JAXP provider [" + factory +
                        "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
                        "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
                pcex.initCause(ex);
                throw pcex;
            }
        }
    }

    return factory;
}

該解析過程呼叫JavaEE標準的JAXP標準進行處理。

至此Spring IoC容器根據定位的Bean定義資原始檔,將其載入讀入並轉換成為Document物件過程完成。

接下來我們要繼續分析Spring IoC容器將載入的Bean定義資原始檔轉換為Document物件之後,是如何將其解析為Spring IoC管理的Bean物件並將其註冊到容器中的。

XmlBeanDefinitionReader解析載入的Bean定義資原始檔

XmlBeanDefinitionReader類中的doLoadBeanDefinitions方法是從特定XML檔案中實際載入Bean定義資源的方法,該方法在載入Bean定義資源之後將其轉換為Document物件,接下來呼叫registerBeanDefinitions啟動Spring IoC容器對Bean定義的解析過程,registerBeanDefinitions方法原始碼如下:

// 按照Spring的Bean語義要求將Bean定義資源解析並轉換為容器內部資料結構 
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    // 解析過程入口,這裡使用了委派模式,具體的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完成  
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;  // 返回此次解析了多少個物件
}

// 建立BeanDefinitionDocumentReader物件,解析Document物件  
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
    return BeanUtils.instantiateClass(this.documentReaderClass);
}

/**
    * Create the {@link XmlReaderContext} to pass over to the document reader.
    */
public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
            this.sourceExtractor, this, getNamespaceHandlerResolver());
}

Bean定義資源的載入解析分為以下兩個過程:

  • 首先,通過呼叫XML解析器將Bean定義資原始檔轉換得到Document物件,但是這些Document物件並沒有按照Spring的Bean規則進行解析。這一步是載入的過程
  • 其次,在完成通用的XML解析之後,按照Spring的Bean規則對Document物件進行解析。

按照Spring的Bean規則對Document物件解析的過程是在介面BeanDefinitionDocumentReader的實現類DefaultBeanDefinitionDocumentReader中實現的。

DefaultBeanDefinitionDocumentReader對Bean定義的Document物件解析

BeanDefinitionDocumentReader介面通過registerBeanDefinitions方法呼叫其實現類DefaultBeanDefinitionDocumentReader對Document物件進行解析,解析的程式碼如下:

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    doRegisterBeanDefinitions(doc.getDocumentElement());
}

// 註冊<beans/>配置的Beans
@SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
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)) {
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            // We cannot use Profiles.of(...) since profile expressions are not supported
            // in XML config. See SPR-12458 for details.
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                            "] not matching: " + getReaderContext().getResource());
                }
                return;
            }
        }
    }

    preProcessXml(root);
    parseBeanDefinitions(root, this.delegate); // 從Document的根元素開始進行Bean定義的Document物件  
    postProcessXml(root);

    this.delegate = parent;
}

BeanDefinitionParserDelegate解析Bean定義資原始檔生成BeanDefinition

/**
    * 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)) {
                    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>元素, 按照Spring的Bean規則解析元素  
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    // 如果元素節點<Beans>元素,即它是巢狀型別的
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // 遞迴解析
        doRegisterBeanDefinitions(ele);
    }
}

解析Bean生成BeanDefinitionHolder的方法

/**
    * Process the given bean element, parsing the bean definition
    * and registering it with the registry.
    */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // 註冊最終的裝飾例項
            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));
    }
}

(這裡就不展開了,無非就是解析XML各種元素,來生成BeanDefinition)

解析過後的BeanDefinition在IoC容器中的註冊

Document物件的解析後得到封裝BeanDefinition的BeanDefinitionHold物件,然後呼叫BeanDefinitionReaderUtils的registerBeanDefinition方法向IoC容器註冊解析的Bean,BeanDefinitionReaderUtils的註冊的原始碼如下:

// 通過BeanDefinitionRegistry將BeanDefinitionHolder註冊到BeanFactory
public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
        throws BeanDefinitionStoreException {

    // Register bean definition under primary name.
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // Register aliases for bean name, if any.
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}

當呼叫BeanDefinitionReaderUtils向IoC容器註冊解析的BeanDefinition時,真正完成註冊功能的是DefaultListableBeanFactory。

DefaultListableBeanFactory向IoC容器註冊解析後的BeanDefinition

IOC容器本質上就是一個beanDefinitionMap, 註冊即將BeanDefinition put到map中

/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

/** Map from bean name to merged BeanDefinitionHolder. */
private final Map<String, BeanDefinitionHolder> mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256);


@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                    "Validation of bean definition failed", ex);
        }
    }

    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    // 如果已經註冊
    if (existingDefinition != null) {
        // 檢查是否可以覆蓋
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
        }
        else if (existingDefinition.getRole() < beanDefinition.getRole()) {
            // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
            if (logger.isInfoEnabled()) {
                logger.info("Overriding user-defined bean definition for bean '" + beanName +
                        "' with a framework-generated bean definition: replacing [" +
                        existingDefinition + "] with [" + beanDefinition + "]");
            }
        }
        else if (!beanDefinition.equals(existingDefinition)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Overriding bean definition for bean '" + beanName +
                        "' with a different definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        }
        else {
            if (logger.isTraceEnabled()) {
                logger.trace("Overriding bean definition for bean '" + beanName +
                        "' with an equivalent definition: replacing [" + existingDefinition +
                        "] with [" + beanDefinition + "]");
            }
        }
        // 覆蓋
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    else {
        if (hasBeanCreationStarted()) {
            // Cannot modify startup-time collection elements anymore (for stable iteration)
            synchronized (this.beanDefinitionMap) {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                removeManualSingletonName(beanName);
            }
        }
        else {
            // Still in startup registration phase
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            removeManualSingletonName(beanName);
        }
        //重置所有已經註冊過的BeanDefinition的快取  
        this.frozenBeanDefinitionNames = null;
    }

    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
    else if (isConfigurationFrozen()) {
        clearByTypeCache();
    }
}

至此,Bean定義資原始檔中配置的Bean被解析過後,已經註冊到IoC容器中,被容器管理起來,真正完成了IoC容器初始化所做的全部工作。現 在IoC容器中已經建立了整個Bean的配置資訊,這些BeanDefinition資訊已經可以使用,並且可以被檢索,IoC容器的作用就是對這些註冊的Bean定義資訊進行處理和維護。這些的註冊的Bean定義資訊是IoC容器控制反轉的基礎,正是有了這些註冊的資料,容器才可以進行依賴注入。

總結

現在通過上面的程式碼,總結一下IOC容器初始化的基本步驟:

  • 初始化的入口在容器實現中的 refresh()呼叫來完成

  • 對 bean 定義載入 IOC 容器使用的方法是 loadBeanDefinition,其中的大致過程如下:

    • 通過 ResourceLoader 來完成資原始檔位置的定位,DefaultResourceLoader 是預設的實現,同時上下文字身就給出了 ResourceLoader 的實現,可以從類路徑,檔案系統, URL 等方式來定為資源位置。如果是 XmlBeanFactory作為 IOC 容器,那麼需要為它指定 bean 定義的資源,也就是說 bean 定義檔案時通過抽象成 Resource 來被 IOC 容器處理的
    • 通過 BeanDefinitionReader來完成定義資訊的解析和 Bean 資訊的註冊, 往往使用的是XmlBeanDefinitionReader 來解析 bean 的 xml 定義檔案 - 實際的處理過程是委託給 BeanDefinitionParserDelegate 來完成的,從而得到 bean 的定義資訊,這些資訊在 Spring 中使用 BeanDefinition 物件來表示 - 這個名字可以讓我們想到loadBeanDefinition,RegisterBeanDefinition 這些相關的方法 - 他們都是為處理 BeanDefinitin 服務的
    • 容器解析得到 BeanDefinition 以後,需要把它在 IOC 容器中註冊,這由 IOC 實現 BeanDefinitionRegistry 介面來實現。註冊過程就是在 IOC 容器內部維護的一個HashMap 來儲存得到的 BeanDefinition 的過程。這個 HashMap 是 IoC 容器持有 bean 資訊的場所,以後對 bean 的操作都是圍繞這個HashMap 來實現的.
  • 然後我們就可以通過 BeanFactory 和 ApplicationContext 來享受到 Spring IOC 的服務了,在使用 IOC 容器的時候,我們注意到除了少量粘合程式碼,絕大多數以正確 IoC 風格編寫的應用程式程式碼完全不用關心如何到達工廠,因為容器將把這些物件與容器管理的其他物件鉤在一起。基本的策略是把工廠放到已知的地方,最好是放在對預期使用的上下文有意義的地方,以及程式碼將實際需要訪問工廠的地方。 Spring 本身提供了對宣告式載入 web 應用程式用法的應用程式上下文,並將其儲存在ServletContext 中的框架實現。

參考文章

https://blog.csdn.net/qq_36212439/article/details/82749963

https://juejin.cn/post/6973884466171215908

https://juejin.cn/post/6844903838743265294

https://blog.csdn.net/hjing123/article/details/104867343

https://www.cnblogs.com/wl20200316/p/12522993.html

更多文章

首先, 從Spring框架的整體架構和組成對整體框架有個認知。

  • Spring基礎 - Spring和Spring框架組成
    • Spring是什麼?它是怎麼誕生的?有哪些主要的元件和核心功能呢? 本文通過這幾個問題幫助你構築Spring和Spring Framework的整體認知。

其次,通過案例引出Spring的核心(IoC和AOP),同時對IoC和AOP進行案例使用分析。

基於Spring框架和IOC,AOP的基礎,為構建上層web應用,需要進一步學習SpringMVC。

  • Spring基礎 - SpringMVC請求流程和案例
    • 前文我們介紹了Spring框架和Spring框架中最為重要的兩個技術點(IOC和AOP),那我們如何更好的構建上層的應用呢(比如web 應用),這便是SpringMVC;Spring MVC是Spring在Spring Container Core和AOP等技術基礎上,遵循上述Web MVC的規範推出的web開發框架,目的是為了簡化Java棧的web開發。 本文主要介紹SpringMVC的請求流程和基礎案例的編寫和執行。

Spring進階 - IoC,AOP以及SpringMVC的原始碼分析

  • Spring進階 - Spring IOC實現原理詳解之IOC體系結構設計
    • 在對IoC有了初步的認知後,我們開始對IOC的實現原理進行深入理解。本文將幫助你站在設計者的角度去看IOC最頂層的結構設計
  • Spring進階 - Spring IOC實現原理詳解之IOC初始化流程
    • 上文,我們看了IOC設計要點和設計結構;緊接著這篇,我們可以看下原始碼的實現了:Spring如何實現將資源配置(以xml配置為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的
  • Spring進階 - Spring IOC實現原理詳解之Bean例項化(生命週期,迴圈依賴等)
    • 上文,我們看了IOC設計要點和設計結構;以及Spring如何實現將資源配置(以xml配置為例)通過載入,解析,生成BeanDefination並註冊到IoC容器中的;容器中存放的是Bean的定義即BeanDefinition放到beanDefinitionMap中,本質上是一個 ConcurrentHashMap<String, Object> ;並且BeanDefinition介面中包含了這個類的Class資訊以及是否是單例等。那麼如何從BeanDefinition中例項化Bean物件呢,這是本文主要研究的內容?
  • Spring進階 - Spring AOP實現原理詳解之切面實現
    • 前文,我們分析了Spring IOC的初始化過程和Bean的生命週期等,而Spring AOP也是基於IOC的Bean載入來實現的。本文主要介紹Spring AOP原理解析的切面實現過程(將切面類的所有切面方法根據使用的註解生成對應Advice,並將Advice連同切入點匹配器和切面類等資訊一併封裝到Advisor,為後續交給代理增強實現做準備的過程)。
  • Spring進階 - Spring AOP實現原理詳解之AOP代理
    • 上文我們介紹了Spring AOP原理解析的切面實現過程(將切面類的所有切面方法根據使用的註解生成對應Advice,並將Advice連同切入點匹配器和切面類等資訊一併封裝到Advisor)。本文在此基礎上繼續介紹,代理(cglib代理和JDK代理)的實現過程。
  • Spring進階 - Spring AOP實現原理詳解之Cglib代理實現
    • 我們在前文中已經介紹了SpringAOP的切面實現和建立動態代理的過程,那麼動態代理是如何工作的呢?本文主要介紹Cglib動態代理的案例和SpringAOP實現的原理。
  • Spring進階 - Spring AOP實現原理詳解之JDK代理實現
    • 上文我們學習了SpringAOP Cglib動態代理的實現,本文主要是SpringAOP JDK動態代理的案例和實現部分。
  • Spring進階 - SpringMVC實現原理之DispatcherServlet初始化的過程
    • 前文我們有了IOC的原始碼基礎以及SpringMVC的基礎,我們便可以進一步深入理解SpringMVC主要實現原理,包含DispatcherServlet的初始化過程和DispatcherServlet處理請求的過程的原始碼解析。本文是第一篇:DispatcherServlet的初始化過程的原始碼解析。
  • Spring進階 - SpringMVC實現原理之DispatcherServlet處理請求的過程
    • 前文我們有了IOC的原始碼基礎以及SpringMVC的基礎,我們便可以進一步深入理解SpringMVC主要實現原理,包含DispatcherServlet的初始化過程和DispatcherServlet處理請求的過程的原始碼解析。本文是第二篇:DispatcherServlet處理請求的過程的原始碼解析。