2.8解析及注册BeanDefinitions

XmlBeanDefinitionReader
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;
}
当文件转换为Document后,接下来提取和注册bean。doc是通过上一节的loadDocument加载转换出来的。BeanDefinitionDocumentReader是接口,而实例化的工作是在createBeanDefinitionDocumentReader中实现的,通过这个方法,beanDefinitionDocumentReader真正的类型其实是DefaultBeanDefinitionDocument-Reader。
DefaultBeanDefinitionDocumentReader
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    doRegisterBeanDefinitions(doc.getDocumentElement());
}
DefaultBeanDefinitionDocumentReader
@SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
    protected void doRegisterBeanDefinitions(Element root) {
        // Any nested <beans> elements will cause recursion in this method. In
        // order to propagate and preserve <beans> default-* attributes correctly,
        // keep track of the current (parent) delegate, which may be null. Create
        // the new (child) delegate with a reference to the parent for fallback purposes,
        // then ultimately reset this.delegate back to its original (parent) reference.
        // this behavior emulates a stack of delegates without actually necessitating one.
        //专门处理解析
        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)) {
                //函数将给定的字符串按照给定的分隔符分隔成字符串数组
                //这里把profile按照,;分隔
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                // We cannot use Profiles.of(...) since profile expressions are not supported
                // in XML config. See SPR-12458 for details.
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("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处理,然后进行解析。可是当我们跟进preProcessXml、post-ProcessXml发现代码是空的。这里使用了模版方法模式()。
 
2.8.1profile属性的使用
profile的使用:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
      xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p"
      xsi:schemaLocation="...">

  <beans profile="dev">
    ...
  </beans>

  <beans profile="prod">
   ...
  </beans>
</beans>
web.xml <context-param> <param>spring.profiles.active</param> <param>prod</param> </context-param>

DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions() if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } }
profile的特性可以让我们在配置文件里配置多种环境,方便进行切换。上述代码首先获取beans节点是否定义了profile属性,如果定义了则会需要到环境变量中寻找。所以这里断言environment不能为空,因为profile是可以同时指定多个,如果encironment中没有定义使用哪个profile则不会浪费性能去解析。
 
2.8.2解析并注册BeanDefinition
处理profile后就可以进行XML的读取了。跟踪代码进入parseBeanDefinition
DefaultBeanDefinitionDocumentReader
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)) {
                    //对bean的处理
                    parseDefaultElement(ele, delegate);
                }
                else {
                    //对bean的处理
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}

 

因为Spring的XMl配置里面有两大类Bean声明,一个是默认的,比如<bean id="test" class="test.person" />。另一类就是自定义的,如:<tx:annotation-driver />。
两种方式的解析与读取差距是巨大的。对于默认的配置spring知道怎么做,但对于自定义的就需要用户实现一些接口及配置。
判断默认的还是自定义的命名空间就是使用node.getNamespaceURI()获取命名空间,并与Spring中固定的命名空间 http://www.springframework.org/schema/beans进行比较。
下一章进行默认标签、自定义标签解析。





posted @ 2021-01-13 16:52  _Shing  阅读(157)  评论(0)    收藏  举报