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

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

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

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取 2000+ 道 Java 面试题

文章引用:

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 进行比对。如果一致则认为是默认,否则认为是自定义的。后续对默认标签解析与自定义标签解析再说明


来源:http://ddrv.cn/a/88268

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring源码学习–解析以及注册BeanDefinitions

评论 抢沙发

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

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

支付宝扫一扫打赏

微信扫一扫打赏