2022-08-23  阅读(46)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/series/article/1453382182

获取 Document 对象后,会根据该对象和 Resource 资源对象调用 registerBeanDefinitions() 方法,开始注册 BeanDefinitions 之旅。如下:

                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;
                }

首先调用 createBeanDefinitionDocumentReader() 方法实例化 BeanDefinitionDocumentReader 对象,然后获取统计前 BeanDefinition 的个数,最后调用 registerBeanDefinitions() 注册 BeanDefinition。 实例化 BeanDefinitionDocumentReader 对象方法如下:

                protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
                    return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
                }

注册 BeanDefinition 的方法 registerBeanDefinitions() 是在接口 BeanDefinitionDocumentReader 中定义,如下:

                void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
                        throws BeanDefinitionStoreException;

从给定的 Document 对象中解析定义的 BeanDefinition 并将他们注册到注册表中 。方法接收两个参数,待解析的 Document 对象,以及解析器的当前上下文,包括目标注册表和被解析的资源。其中 readerContext 是根据 Resource 来创建的,如下:

                public XmlReaderContext createReaderContext(Resource resource) {
                    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                            this.sourceExtractor, this, getNamespaceHandlerResolver());
                }

DefaultBeanDefinitionDocumentReader 对该方法提供了实现:

                public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
                    this.readerContext = readerContext;
                    logger.debug("Loading bean definitions");
                    Element root = doc.getDocumentElement();
                    doRegisterBeanDefinitions(root);
                }

调用 doRegisterBeanDefinitions() 开启注册 BeanDefinition 之旅。

                protected void doRegisterBeanDefinitions(Element root) {
                    BeanDefinitionParserDelegate parent = this.delegate;
                    this.delegate = createDelegate(getReaderContext(), root, parent);
            
                    if (this.delegate.isDefaultNamespace(root)) {
                          // 处理 profile
                        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
                        if (StringUtils.hasText(profileSpec)) {
                            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                                if (logger.isInfoEnabled()) {
                                    logger.info("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;
                }

程序首先处理 profile属性,profile主要用于我们切换环境,比如切换开发、测试、生产环境,非常方便。然后调用 parseBeanDefinitions() 进行解析动作,不过在该方法之前之后分别调用 preProcessXml()postProcessXml() 方法来进行前、后处理,目前这两个方法都是空实现,交由子类来实现。

                protected void preProcessXml(Element root) {
                }
            
                protected void postProcessXml(Element root) {
                }

parseBeanDefinitions() 定义如下:

                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);
                    }
                }

最终解析动作落地在两个方法处:parseDefaultElement(ele, delegate)delegate.parseCustomElement(root)。我们知道在 Spring 有两种 Bean 声明方式:

  • 配置文件式声明:<bean id="studentService" class="org.springframework.core.StudentService"/>
  • 自定义注解方式:<tx:annotation-driven>

两种方式的读取和解析都存在较大的差异,所以采用不同的解析方法,如果根节点或者子节点采用默认命名空间的话,则调用 parseDefaultElement() 进行解析,否则调用 delegate.parseCustomElement() 方法进行自定义解析。 至此,doLoadBeanDefinitions() 中做的三件事情已经全部分析完毕,下面将对 Bean 的解析过程做详细分析说明。


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文