Spring源码学习--解析以及注册BeanDefinitions

8人阅读 评论(0) 收藏 举报
分类:

文章引用:

https://www.cnblogs.com/wade-luffy/p/6066932.html(Spring的profile属性)
https://www.cnblogs.com/SummerinShire/p/6392242.html

继续上一遍文章Spring源码学习–获取Document(https://blog.csdn.net/u013412772/article/details/79833030),Spring将文件转换为Document后,接下来就是提取以及注册Bean;此时程序已经拥有XML文档文件的Document实例对象了。


一、XmlBeanDefinitionReader

    /**
     * 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 {
        // 使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

        // 在实例化BeanDefinitionReader时候会将BeanDefinitionRegistry传入,默认使用继承自DefaultListableBeanFactory的子类
        // 记录统计前BeanDefinition的加载个数
        int countBefore = getRegistry().getBeanDefinitionCount();

        // 加载以注册BeanDefinition个数
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

        // 记录本次加载的BeanDefinition个数
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

其中registerBeanDefinitions方法的第一个参数是Spring源码学习–获取Document(https://blog.csdn.net/u013412772/article/details/79833030)的loadDocument加载转换出来的。在这个方法中很好的应用了面向对象中单一职责的原则,将逻辑处理委托给单一的类进行处理,而这个逻辑处理类就是BeanDefinitionDocumentReader。其中BeanDefinitionDocumentReader如下所示:

public interface BeanDefinitionDocumentReader {

    /**
     * Read bean definitions from the given DOM document and
     * register them with the registry in the given reader context.
     * @param doc the DOM document
     * @param readerContext the current context of the reader
     * (includes the target registry and the resource being parsed)
     * @throws BeanDefinitionStoreException in case of parsing errors
     */
    void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
            throws BeanDefinitionStoreException;

}

而实例化的工作实在createBeanDefinitionDocumentReader()中完成的,而通过此方法,BeanDefinitionDocumentReader真正的类型其实已经是DefaultBeanDefinitionDocumentReader类了,进入DefaultBeanDefinitionDocumentReader类中查看registerBeanDefinitions()的方法代码如下:

    /**
     * This implementation parses bean definitions according to the "spring-beans" XSD
     * (or DTD, historically).
     * <p>Opens a DOM Document; then initializes the default settings
     * specified at the {@code <beans/>} level; then parses the contained bean definitions.
     */
    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }

发现这个方法的重要目的之一就是提取xml文件中的root,以便于再次将root作为参数继续BeanDefinition的注册。经过上面的分析,我们终于到了核心逻辑的底部

protected void doRegisterBeanDefinitions(Element root)

如果说以前都是在XML加载解析的准备阶段,那么doRegisterBeanDefinitions算是真正的开始解析了,doRegisterBeanDefinitions方法的实现如下面所示:

/**
     * Register each bean definition within the given root {@code <beans/>} element.
     */
    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;
    }

通过上面的代码我们可以开发处理流程,发现preProcessXml(root)和postProcessXml(root)方法代码是空的,既然是空的为什么还要写?就像面向对象设计方法学中说的一句话,一个类要么面向继承设计,要么用final修饰。在DefaultBeanDefinitionDocumentReader中并没有用final修饰,所以它是面向继承而设计的。这两个方法正式为子类设计的,如果了解设计模式,会发现这个正是模版方法模式,如果继承自DefaultBeanDefinitionDocumentReader的子类需要在Bean解析前后做一些特殊的处理,那么只需要重写这两个方法就好了。

二、profile属性

具体profile的使用可以参照:https://www.cnblogs.com/SummerinShire/p/6392242.html

profile属性的含义:通过profile标记不同的环境,可以通过设置spring.profiles.active和spring.profiles.default激活指定profile环境。如果设置了active,default便失去了作用。如果两个都没有设置,那么带有profiles的bean都不会生成。有多种方式来设置这两个属性:

作为DispatcherServlet的初始化参数;
作为web应用的上下文参数;
作为JNDI条目;
作为环境变量; System.set("spring.profiles.active","prod")
作为JVM的系统属性; -Dspring.profiles.active="prod"
在集成测试类上,使用@ActiveProfiles注解配置。

三、解析并注册BeanDefinition

进过上面的分析,开始进行XML的读取,跟踪代码进入parseBeanDefinitions方法如下:

    /**
     * Parse the elements at the root level in the document:
     * "import", "alias", "bean".
     * @param root the DOM root element of the document
     */
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        // 对beans的处理
        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)) {
                        // 对bean的处理
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        // 对bean的处理
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

在Spring中XML配置文件里面有两大类Bean声明,一类是默认的,如下所示:

    <!-- 解决框架对url参数中含有.的string类型参数截断问题 -->
    <bean id="handlerMapping"
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
        <property name="useSuffixPatternMatch" value="false" />
    </bean>

另一类是自定义的如下所示:

<tx:annotation-driven/>

Spring针对这两种情况分别解析,如果采用Spring默认的配置,Spring当然之后该怎么解析,如果采用自定义,那么就需要用户实现一些接口以及配置了。对于根节点或者子节点如果是默认命名空间的话则采用parseDefaultElement方法进行解析,否则使用delegate.parseCustomElement(root);方法对自定义命名空间进行解析。二判断是否默认命名空间还是自定义命名空间的办法其实是使用node.getNamespaceUTl()获取命名空间,并与Spring中固定的命名空间 http://www.springframework.org/schema/beans 进行比对。如果一致则认为是默认,否则认为是自定义的。后续对默认标签解析与自定义标签解析再说明

查看评论

Spring源码阅读之-自定义配置的解析

Spring源码阅读之-自定义配置的解析上文书:《Spring源码阅读之-bean的解析与注册》说到,Spring根据delegate.parseCustomElement(ele); 方法来解析自定...
  • weiythi
  • weiythi
  • 2017-07-23 12:36:11
  • 310

【spring源码分析】--Bean的解析与注册

Bean的具体解析以及如何将Bean注册到IOC容器中
  • ZSMJ_2011
  • ZSMJ_2011
  • 2015-07-07 22:32:02
  • 1578

解析及注册BeanDefinitions。

最近在研究Spring源码,博文中的内容来源《Spring源码深度解析》这一本书,感激不尽。...
  • en_joker
  • en_joker
  • 2017-06-20 23:14:00
  • 115

【Spring源码--IOC容器的实现】(三)BeanDefinition的载入和解析【I】

一步步解析源码,本篇文章分析到Xml中bean常用配置的解析
  • u013185616
  • u013185616
  • 2016-08-15 21:38:14
  • 1961

spring4.0 源码分析 搭建简单的分析环境(一)

spring4.0 源码分析 搭建简单的分析环境
  • sun_aichao
  • sun_aichao
  • 2015-12-13 11:21:56
  • 3482

Spring源码深度解析(八)解析及注册BeanDefinitions

从下面开始说: 这段代码还比较清晰,在Spirng的配置里面有两大类Bean声明,一个是默认的,如: 另一类就是自定义的,如: 两种方式解析差别还挺大的,如果采用Spring默认...
  • u012291108
  • u012291108
  • 2016-06-30 19:46:58
  • 2208

《spring源码深度解析》读书笔记之——环境搭建

spring源码深度解析之——环境搭建
  • Prepared
  • Prepared
  • 2017-07-08 13:14:37
  • 677

spring源码深度解析,高清版

  • 2017年12月26日 17:29
  • 94.96MB
  • 下载

Spring源码学习文档,绝对值得好好研究~~

  • 2011年05月10日 10:08
  • 186KB
  • 下载

spring源码剖析(二)Spring默认标签解析及注册实现

spring默认标签解析及注册实现
  • fighterandknight
  • fighterandknight
  • 2016-05-05 00:21:53
  • 8867
    个人资料
    专栏达人
    等级:
    访问量: 10万+
    积分: 1427
    排名: 3万+
    博客专栏
    最新评论