【spring源码分析】-容器基础

最近在看spring源码,配合了两本书,《spring源码深度解析》和《spring技术内幕》,主要想对spring有更深入地了解。
首先对spring基础容器进行分析:
XmlBeanFactory beanFactory=new XmlBeanFactory(new ClasspathResource("spring.xml"));
上面的代码是我们在使用spring时最先写的一句代码,这段代码就是创建了一个spring容器,那么我们就跟踪这段代码来分析spring的容器。

DefaultListableBeanFactory

DefaultListableBeanFactory是我们要分析的类,XmlBeanFactory继承了DefaultListableBeanFactory类,扩展了DefaultListableBeanFactory类可以进行自定义xml读取。

XmlBeanDefinitionReader

XmlBeanDefinitionReader是spring对xml配置文件读取的类,其主要功能如下:
* 使用ResourceLoader将资源文件路径转换为对应的Resource对象
* 通过DocumentLoader对Resource文件进行转换,讲Resource文件转换为Document文件
* 通过实现接口BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader对Document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析

Created with Raphaël 2.1.2 BeanFactoryTest BeanFactoryTest ClassPathResource ClassPathResource XmlBeanFactory XmlBeanFactory XmlBeanDefinitionReader XmlBeanDefinitionReader 1.new ClassPathResource(“beans.xml”) 2.resource 3.new XmlBeanFactory(resource) 3.1 loadBeanDefinitions(resource) 3.2 int 4.BeanFactory

从时序图我们可以看出整个逻辑处理顺序首先调用调用ClassPathResource的构造函数来构造Resource资源文件的实例对象

加载Bean

我们来进入详细的代码片段,来看一下如何加载bean。前面分析了,其实重要的逻辑都在XmlBeanDefinitionReader的loadBeanDefinitions方法中,那我们来看下这个方法的代码:

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.getResource());
        }
//检查是否重复加载xml配置
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<EncodedResource>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
        //获取封装的Resource对象的流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
            //创建org.xml.sax.InputSource对象,这里我们看出spring采用sax方式来读取xml哦
                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();
            }
        }
    }

真正的处理逻辑在doLoadBeanDefinitions方法中,顺藤摸瓜看看里面的代码:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
        //加载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);
        }
    }

首先看如何得到Document对象:

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }

这里说一下loadDocument方法的各个参数:
* inputSource,不说了,前面SAX要读取的流对象
* 第二个getEntityResolver()是用来获取DTD或者XSD的方法
* errorHandler,错误处理
* getValidationModeForResource(resource)获取xml的验证方式
* isNamespaceAware() 我还没明白。。。。

当把文件转换为Document后,接下来的提取以及注册bean就是重头戏了。我们接着上面的代码进行分析registerBeanDefinitions方法:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader 
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//将环境变量设置其中 
documentReader.setEnvironment(this.getEnvironment());
//在实例化BeanDefinitionBeanReader时会将BeanDefinitionRegistry传入,默认使用继承自DefaultlistableBeanFactory的子类
//记录统计前BeanDefinition的加载个数
        int countBefore = getRegistry().getBeanDefinitionCount();
        //加载及注册bean
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        //返回本次加载的BeanDefinition个数
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

好了,离核心代码不远了,我们继续看DefaultlistableBeanFactory的registerBeanDefinitions方法:

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        //提取Document的root
        Element root = doc.getDocumentElement();
        //然后这才是真正的注册bean,oh mygod
        doRegisterBeanDefinitions(root);
    }

继续深入doRegisterBeanDefinitions方法:

    protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(this.readerContext, root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
        //处理root的profile属性
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!this.environment.acceptsProfiles(specifiedProfiles)) {
                    return;
                }
            }
        }
        //解析前的处理,这个方法留给子类实现
        preProcessXml(root);
        //主要的逻辑在这里
        parseBeanDefinitions(root, this.delegate);
        //解析后的处理,这个方法也留给子类实现的
        postProcessXml(root);

        this.delegate = parent;
    }

我们这里有个疑问了,root的profile属性是干嘛的啊?其实我也没用过,差了资料后才知道,这个属性是spring提供给开发者可以在同一个文件部署两套配置,比如我们配置一个开发时使用的配置、上线一个配置,完全可以使用<beans profile="dev">...</beans><beans profile="product">..</beans>来进行配置。
好了,接下来分析parseBeanDefinitions方法了。。。。下一篇吧,我需要理一下头绪,感觉像进了迷宫,需要静一静。。。

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/zsmj_2011/article/details/45081553
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞