关于spring:Spring框架系列7-Spring-IOC实现原理详解之IOC初始化流程

0次阅读

共计 25467 个字符,预计需要花费 64 分钟才能阅读完成。

上文,咱们看了 IOC 设计要点和设计构造;紧接着这篇,咱们能够看下源码的实现了:Spring 如何实现将资源配置(以 xml 配置为例)通过加载,解析,生成 BeanDefination 并注册到 IoC 容器中的。@pdai

  • Spring 框架系列(7) – Spring IOC 实现原理详解之 IOC 初始化流程

    • 引入
    • 如何将 Bean 从 XML 配置中解析后放到 IoC 容器中的?

      • 初始化的入口
      • 设置资源解析器和环境
      • 设置配置门路
      • 初始化的主体流程

        • 初始化 BeanFactory 之 obtainFreshBeanFactory
        • 初始化 BeanFactory 之 loadBeanDefinitions
        • AbstractBeanDefinitionReader 读取 Bean 定义资源
        • XmlBeanDefinitionReader 加载 Bean 定义资源
        • DocumentLoader 将 Bean 定义资源转换为 Document 对象
        • XmlBeanDefinitionReader 解析载入的 Bean 定义资源文件
        • DefaultBeanDefinitionDocumentReader 对 Bean 定义的 Document 对象解析
        • BeanDefinitionParserDelegate 解析 Bean 定义资源文件生成 BeanDefinition
        • 解析过后的 BeanDefinition 在 IoC 容器中的注册
        • DefaultListableBeanFactory 向 IoC 容器注册解析后的 BeanDefinition
    • 总结
    • 参考文章
    • 更多文章

引入

上文,咱们看了 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();}
    }
}

<mark> 这里的设计上是一个十分典型的资源类加载解决型的思路,头脑中须要造成如下图的 顶层思路</mark>(而不是只停留在流水式的办法下面):

  • 模板办法设计模式 ,模板办法中应用典型的 钩子办法
  • 具体的初始化加载办法 插入到钩子办法之间
  • 将初始化的阶段封装,用来记录以后初始化到什么阶段;常见的设计是 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_3621…

https://juejin.cn/post/697388…

https://juejin.cn/post/684490…

https://blog.csdn.net/hjing12…

https://www.cnblogs.com/wl202…

更多文章

首先,从 Spring 框架的整体架构和组成对整体框架有个认知。

  • Spring 根底 – Spring 和 Spring 框架组成

    • Spring 是什么?它是怎么诞生的?有哪些次要的组件和外围性能呢? 本文通过这几个问题帮忙你构筑 Spring 和 Spring Framework 的整体认知。

其次,通过案例引出 Spring 的外围(IoC 和 AOP),同时对 IoC 和 AOP 进行案例应用剖析。

  • Spring 根底 – Spring 简略例子引入 Spring 的外围

    • 上文中咱们简略介绍了 Spring 和 Spring Framework 的组件,那么这些 Spring Framework 组件是如何配合工作的呢?本文次要承接上文,向你展现 Spring Framework 组件的典型利用场景和基于这个场景设计出的简略案例,并以此引出 Spring 的外围要点,比方 IOC 和 AOP 等;在此基础上还引入了不同的配置形式,如 XML,Java 配置和注解形式的差别。
  • Spring 根底 – Spring 外围之管制反转(IOC)

    • 在 Spring 根底 – Spring 简略例子引入 Spring 的外围中向你展现了 IoC 的根底含意,同时以此发散了一些 IoC 相干知识点; 本节将在此基础上进一步解读 IOC 的含意以及 IOC 的应用形式
  • Spring 根底 – Spring 外围之面向切面编程(AOP)

    • 在 Spring 根底 – Spring 简略例子引入 Spring 的外围中向你展现了 AOP 的根底含意,同时以此发散了一些 AOP 相干知识点; 本节将在此基础上进一步解读 AOP 的含意以及 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 解决申请的过程的源码解析。
正文完
 0