阅读 213

Spring bean加载过程解析(下)

2.资源解析阶段

需要将读取器与工厂以及资源对象进行关联处理

2.1loadBeanDefinitions(Resource resource)方法简介

2.1.1BeanDefinitionReader接口中的oadBeanDefinitions(Resource resource)

  /**
     * Load bean definitions from the specified resource.
     * @param resource the resource descriptor
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     */

    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
复制代码

2.1.2XmlBeanDefinitionReader

该类作为 AbstractBeanDefinitionReader 的扩展类,继承了 AbstractBeanDefinitionReader 所有的方法,同时也扩展了很多新的方法,主要用于读取 XML 文件中定义的 bean。具体使用如下:

@Test
public void getBeanDefinition(){
    ClassPathResource resource = new ClassPathResource("application_context.xml");
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
    reader.loadBeanDefinitions(resource);
}
复制代码

这段代码是 Spring 中编程式使用 IOC 容器,通过这四段简单的代码,我们可以初步判断 IOC 容器的使用过程。

  • 获取资源
  • 获取 BeanFactory
  • 根据新建的 BeanFactory 创建一个BeanDefinitionReader对象,该Reader 对象为资源的解析器
  • 装载资源 整个过程就分为三个步骤:资源定位、装载、注册,如下:

资源定位。我们一般用外部资源来定义 Bean 对象,所以在初始化 IoC 容器的第一步就是需要定位这个外部资源。在 Spring IoC资源管理的两篇文章中已经详细说明了资源加载的过程。

装载。装载就是 BeanDefinition 的载入,BeanDefinitionReader 读取、解析 Resource 资源,也就是将用户定义的 Bean 表示成 IoC 容器 的内部数据结构:BeanDefinition。在 IoC 容器内部维护着一个 BeanDefinition Map 的数据结构,在配置文件中每一个都对应着一个 BeanDefinition 对象。

注册。向 IoC 容器注册在第二步解析好的 BeanDefinition,这个过程是通过 BeanDefinitionRegistry 接口来实现的。本质上是将解析得到的 BeanDefinition 注入到一个 HashMap 容器中,IoC 容器就是通过这个 HashMap HashMap 来维护这些 BeanDefinition 的。注意:此过程并没有完成依赖注入,依赖注册是发生在应用第一次调用 getBean()向容器索要 Bean 时。当然我们可以通过设置预处理,即对某个 Bean 设置 lazyInit 属性

/**
     * Load bean definitions from the specified XML file.
     * @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
     */
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        //EncodedResource封装的是真正的待解析的资源
        return loadBeanDefinitions(new EncodedResource(resource));
    }

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    return this.loadBeanDefinitions(new EncodedResource(resource));
}

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Loading XML bean definitions from " + encodedResource);
    }

    //获取已经被加载的资源集合中的资源集合,如果为null,则开辟空间
    Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }

    //判断currentResources中是否包含encodedResource,如果有则抛出异常,没有则加入
    if (!((Set)currentResources).add(encodedResource)) {
        throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    } else {
        int var5;
        try {
            //获取Resource对应的字节流
            InputStream inputStream = encodedResource.getResource().getInputStream();

            try {
                //使用字节流创建新的输入源
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    //设置编码
                    inputSource.setEncoding(encodedResource.getEncoding());
                }

                //该方法就是创建BeanDefinition的关键
                var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            } finally {
                inputStream.close();
            }
        } catch (IOException var15) {
            throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
        } finally {
            ((Set)currentResources).remove(encodedResource);
            if (((Set)currentResources).isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }

        }

        return var5;
    }
}
复制代码

loadBeanDefinitions(resource) 是加载资源的真正实现,从指定的 XML 文件加载 Bean Definition,这里会对 Resource 封装成 EncodedResource,主要是为了对 Resource 进行编码,保证内容读取的正确性。封装成 EncodedResource 后,调用 loadBeanDefinitions(encodedResource)。

首先通过 resourcesCurrentlyBeingLoaded.get() 来获取已经加载过的资源,然后将 encodedResource 加入其中,如果 resourcesCurrentlyBeingLoaded 中已经存在该资源,则抛出 BeanDefinitionStoreException 异常。完成后从 encodedResource 获取封装的 Resource 资源并从 Resource 中获取相应的 InputStream ,最后将 InputStream 封装为 InputSource 调用 doLoadBeanDefinitions()。方法 doLoadBeanDefinitions() 为从 xml 文件中加载 Bean Definition 的真正逻辑

2.1.2.1bean import标签

<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-2.5.xsd">
    
    <import resource="xmlfolder/app1.xml" />
</beans>
复制代码

2.1.2.2InputSource inputSource = new InputSource(inputStream);

InputSource位于package org.xml.sax;

关于解析xml的两种方式 dom(树形结构,先创建好,再去解析节点)和sax(流的形式,一边读取一边解析),spring使用的是sax对象

    /*
     * @param byteStream The raw byte stream containing the document.
     * @see #setPublicId
     * @see #setSystemId
     * @see #setEncoding
     * @see #setByteStream
     * @see #setCharacterStream
     */
    public InputSource (InputStream byteStream)
    {
        setByteStream(byteStream);
    }
复制代码

2.1.2.3doLoadBeanDefinitions(inputSource, encodedResource.getResource());

关于解析xml的两种方式 dom(树形结构,先创建好,再去解析节点)和sax(流的形式,一边读取一边解析),spring使用的是sax对象

/**
     * 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其实对应的是一个document对象,类似于html这样子;即这行代码就是为了得到xml对应的document对象
            Document doc = doLoadDocument(inputSource, resource);
            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);
        }
    }
复制代码

2.1.2.3.1 registerBeanDefinitions(doc, resource); 随后返回

/**
     * 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();
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
复制代码

3.获取Bean的阶段

获取到完整的 BeanDefintion 后,就可以拿这份定义信息来实例具体的 Bean。

具体实例创建见 AbstractAutowireCapableBeanFactory.createBeanInstance ,返回 Bean 的包装类 BeanWrapper,一共有三种策略:

  • 使用工厂方法创建,instantiateUsingFactoryMethod 。
  • 使用有参构造函数创建,autowireConstructor。
  • 使用无参构造函数创建,instantiateBean。

end~

文章分类
后端
文章标签