1.前言

2.spring源码obtainFreshBeanFactory()介绍

3.总结

1.前言

github源码地址(带注释):
https://github.com/su15967456...

我们上篇博客对spring的核心方法有了一个大概的认知,从今往后的几篇博客,我们将会将这几个方法进行深入地分析。

话不多说,先上图。

image.png

今天我们要介绍的obtainFreshBeanFactory()方法,其主要功能就是:
1.创建容器对象 DefaultListableBeanFactory
2.加载各种配置文件的属性到当前工厂中,最重要的就是封装成BeanDefinition

2.spring源码obtainFreshBeanFactory()介绍

接下来我们来分析一下这个方法,首先往这个方法里点击:

    /**
     * Tell the subclass to refresh the internal bean factory.
     * @return the fresh BeanFactory instance
     * @see #refreshBeanFactory()
     * @see #getBeanFactory()
     */
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //初始化BeanFactory,并进行xml文件读取,并将得到的BeanFactory记录在当前实体的属性中
        refreshBeanFactory();
        return getBeanFactory();//返回当前实体的beanFactory属性
    }

注释上告诉我们:主要就是生成一个bean工厂,我们可以继续观察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 {
        //如果有bean工厂了,先销毁掉
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //创建DefaultListableBeanFactory对象
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            //每个容器都有自己的id,为了序列化指定id,可以从id反序列化到beanFactory对象
            beanFactory.setSerializationId(getId());
            //定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖,可以通过子类重写
            customizeBeanFactory(beanFactory);
            //初始化documentReader,并进行对xml文件进行解析
            loadBeanDefinitions(beanFactory);
            this.beanFactory = beanFactory;
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

我们可以一目了然地看到,这个方法的前几步是十分简单而明了的:
1.创建一个bean工厂对象
2.设置一下容器的id
3.设置一下beanFactory的相关属性(包括是否允许覆盖同名称的不同定义的对象以及循环依赖,可以通过子类重写) 如果不清楚括号内容也没关系,主要就是有些属性以后要用到,我们这里要先进行一下初始化
4.对xml文件进行解析(这是一个封装方法,我们还要往里面继续查看,看看spring是如何将xml文件读取到容器中的)

我们继续debug,进入loadBeanDefinitions(beanFactory)方法;

/**
     * 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.
        //适配器模式
        //创建一个xml的beanDefinitionReader,并通过回调设置到beanFactory中
        //beanFactory和applicationContext没有办法直接读取xml,就交给beanDefinitionReader进行,这就是适配器模式
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        // 给reader对象设置环境对象
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        //设置一个entity,用它来读取本地的xsd或者dtd文件,来完成相关的解析工作
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        //设计模式,适配器模式
        //初始化beanDefinitionReader对象,此处设置配置文件是否需要验证
        initBeanDefinitionReader(beanDefinitionReader);
        //开始完成beanDefinition的加载
        loadBeanDefinitions(beanDefinitionReader);
    }

可以看出,大概进行了如下的操作:
创建一个XmlBeanDefinitionReader,通过XmlBeanDefinitionReader来完成beanDefinition的加载
这里使用了适配器模式:就是beanFactory本身没办法进行xml文件(配置文件)的读取,所以要借助beanDefinitionReader类进行对配置文件的读取,要让beanDefinitionReader做一个适配。(就像手机没办法直接从插座中获取电源,要借助适配器来充电。)
所以把beanFactory交给beanDefinitionReader,让beanDefinitionReader读取文件,封装成beanDefinition,加入beanFactory容器中。

/**
     * 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对象,这个解析过程是由documentLoader完成的,
            // 从string[] -> StringResources -> resources
            // 最终将resources解析成一个个document文档,根据文档信息封装成BeanDefinition对象
            Document doc = doLoadDocument(inputSource, resource);
            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);
        }
    }

然后我们一直往loadBeanDefinitions(beanDefinitionReader);里debug,会发现两个方法:
1.doLoadDocument(inputSource, resource);
2.registerBeanDefinitions(doc, resource);

注意,spring里面do开头的方法才是做实事的方法

我们来看doLoadDocument(inputSource, resource):
它主要做了两件事
1)将resources解析成一个个document文档
2)根据这个文档信息封装成BeanDefinition对象

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

        try {
            // 此处获取xml文件的document对象,这个解析过程是由documentLoader完成的,
            // 从string[] -> StringResources -> resources
            // 最终将resources解析成一个个document文档,根据文档信息封装成BeanDefinition对象
            Document doc = doLoadDocument(inputSource, resource);
            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);
        }
    }

我们看看 registerBeanDefinitions(Document doc, Resource resource) 方法:

它也主要做两件事情
1)对xml的封装成的Document进行解析(Document->封装成BeanDefinition)
2)完成BeanDefinition的注册

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //对xml的beanDefinition进行解析
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();//获取当前bean的数量
        //完成具体的解析过程
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
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);//根据根节点利用解析器解析元素
        postProcessXml(root);

        this.delegate = parent;
    }
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)) {//默认的命名空间,import,bean,alias这些,可以解析,如果是其它标签,需要额外的解析器
                        parseDefaultElement(ele, delegate);
                    } else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

3.总结
今天我们大概总结了一下obtainFreshBeanFactory()方法,该方法主要有两个作用:

1.创建容器对象 DefaultListableBeanFactory
2.加载各种配置文件的属性到当前工厂中,封装成BeanDefinition


苏凌峰
73 声望38 粉丝

你的迷惑在于想得太多而书读的太少。