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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

最近在看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方法了。。。。下一篇吧,我需要理一下头绪,感觉像进了迷宫,需要静一静。。。


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » 【spring源码分析】-容器基础

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏