spring IOC篇二:xml的核心逻辑处理(doLoadBeanDefinitions(inputSource, encodedResource.getResource()))

Posted 烟尘

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring IOC篇二:xml的核心逻辑处理(doLoadBeanDefinitions(inputSource, encodedResource.getResource()))相关的知识,希望对你有一定的参考价值。

2.1 doLoadBeanDefinitions(inputSource, encodedResource.getResource())

/**
     * Actually load bean definitions from the specified XML file.
     * @param inputSource the SAX InputSource to read from
     * @param resource the resource descriptor for the XML file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     * @see #doLoadDocument
     * @see #registerBeanDefinitions
     */
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
             //获取XML的验证方式,加载XML文件得到对应的Document
            Document doc = doLoadDocument(inputSource, resource);
            //根据返回的Dcoument注册Bean信息
            return registerBeanDefinitions(doc, resource);
        } 
       .........................................
   } 

2,2 Document doc = doLoadDocument(inputSource, resource);

 /**
     * Actually load the specified document using the configured DocumentLoader.
     * @param inputSource the SAX InputSource to read from
     * @param resource the resource descriptor for the XML file
     * @return the DOM Document
     * @throws Exception when thrown from the DocumentLoader
     * @see #setDocumentLoader
     * @see DocumentLoader#loadDocument
     */
    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }

2.2.3  getValidationModeForResource(resource) DTD或者XSD文件格式的读取

 

/**
     * Gets the validation mode for the specified {@link Resource}. If no explicit
     * validation mode has been configured then the validation mode is
     * {@link #detectValidationMode detected}.
     * <p>Override this method if you would like full control over the validation
     * mode, even when something other than {@link #VALIDATION_AUTO} was set.
     */
    protected int getValidationModeForResource(Resource resource) {
        int validationModeToUse = getValidationMode();
    // 如果手动指定了校验模式,则使用指定的校验模式    
    if (validationModeToUse != VALIDATION_AUTO) {
            return validationModeToUse;
        }
// 如果手动没有指定校验模式,则使用自动检测

        int detectedMode = detectValidationMode(resource);
        if (detectedMode != VALIDATION_AUTO) {
            return detectedMode;
        }
        // Hmm, we didn‘t get a clear indication... Let‘s assume XSD,
        // since apparently no DTD declaration has been found up until
        // detection stopped (before finding the document‘s root tag).
        return VALIDATION_XSD;
    }

 

自动检测的代码逻辑分析:

先定义一个变量 isDtdValidated 是否是DTD约束 默认是 false,然后一行行的读取xml资源文件,如果里面包含 DOCTYPE  字符串则是DTD约束,则上返回,停止自动检测。

 

int detectedMode = detectValidationMode(resource);
public int detectValidationMode(InputStream inputStream) throws IOException {
        // Peek into the file to look for DOCTYPE.
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        try {
            
            boolean isDtdValidated = false;
            String content;
            while ((content = reader.readLine()) != null) {
                content = consumeCommentTokens(content);
                // 如果读取的行是空行或者注释,则省略
                if (this.inComment || !StringUtils.hasText(content)) {
                    continue;
                }
                // 判断是否含有 DOCTYPE 标签
                if (hasDoctype(content)) {
                    isDtdValidated = true;
                    break;
                }
                if (hasOpeningTag(content)) {
                    // End of meaningful data...
                    break;
                }
            }
            return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
        }

 

2,2,4 getEntityResolver() 获取资源文件约束的声明

 

entityResolver接口有以下一个方法

 

public abstract InputSource resolveEntity (String publicId,
                                               String systemId)
        throws SAXException, IOException;

}

 

他接受两个参数:publicIdsystemId,并返回InputSource 对象

 

如果是XSD资源文件:
<?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:cutesource="http://blog.csdn.net/cutesource/schema/people"  
    xsi:schemaLocation="  
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
http://blog.csdn.net/cutesource/schema/people http://blog.csdn.net/cutesource/schema/people.xsd">  
    <cutesource:people id="cutesource" name="袁志俊" age="27"/>  
</beans> 
将会读取到以下两个参数:
publicId:null
systemId:http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
如果是 DTD约束:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper>
...............................
</mapper>
将会读取到以下两个参数:
publicId:-//mybatis.org//DTD
systemId:http://mybatis.org/dtd/mybatis-3-mapper.dtd
通过getEntityResolver方法获取EntityResolver接口对象,DelegatingEntityResolver为EntityResolver的实现类。

protected EntityResolver getEntityResolver() {
        if (this.entityResolver == null) {
            // Determine default EntityResolver to use.
            ResourceLoader resourceLoader = getResourceLoader();
            if (resourceLoader != null) {
    如果资源加载器不为空,则使用ResourceEntityResolver对象进行文件约束的声明
                this.entityResolver = new ResourceEntityResolver(resourceLoader);
            }
            else {
                this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
            }
        }
        return this.entityResolver;
    }

 

2.2.5 new ResourceEntityResolver(resourceLoader)

public DelegatingEntityResolver(ClassLoader classLoader) {
        // 判断是否是DTD约束
           this.dtdResolver = new BeansDtdResolver();
       //  如果是XSD约束 在获取声明的默认路经是:
      // META-INF/spring.schemas
        this.schemaResolver = new PluggableSchemaResolver(classLoader);
    }

 

2.3 解析并注册 BeanDefinitions

 

 将文件装换为Document以后,接下来的提取以及注册Bean就是我们的重头戏。

 

/**
     * 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();
// 记录统计前BeanDefinition的加载个数
        int countBefore = getRegistry().getBeanDefinitionCount();
// 加载以及注册Bean

 //  这里使用到了单一职责原则,将逻辑处理委托给单一的类进行处理,这个逻辑处理类就是 BeanDefinitionDocumentReader 对象

        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 统计本次加载Beanfinition的个数
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

 

继续深入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);
    }

 

继续深入doRegisterBeanDefinitions方法:doRegisterBeanDefinitions算开始真正解析XML文件了。

 

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);
       // 处理profile 属性
        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);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    return;
                }
            }
        }
         // 空代码留给子类去实现 模板设计模式 继承 DefaultBeanDefinitionDocumentReader 的子类咋XML解析前做一些处理,可以实现此方法
        preProcessXml(root);
        // 解析除了 profile以外的属性
        parseBeanDefinitions(root, this.delegate);
        // 空代码留给子类去实现 模板设计模式 继承 DefaultBeanDefinitionDocumentReader 的子类咋XML解析后做一些处理,可以实现此方法
        
        postProcessXml(root);

        this.delegate = parent;
    }

 

2.4 判断标签的解析方式

 

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
      // 对Bean的处理
        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);
        }
    }

 

以上是关于spring IOC篇二:xml的核心逻辑处理(doLoadBeanDefinitions(inputSource, encodedResource.getResource()))的主要内容,如果未能解决你的问题,请参考以下文章

死磕 Spring----- IOC 之 获取验证模型

整理在Spring IOC容器初始化后可以处理特定逻辑的多种实现方式

spring源码之IOC的非核心部分

Spring源码剖析1:初探Spring IOC核心流程

Spring IOC 相关配置-总结

Spring(概念和ioc入门ioc操作xml配置文件)