zoukankan      html  css  js  c++  java
  • 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);
            }
        }
  • 相关阅读:
    Docker
    springboot与缓存
    微信小程序资源
    Docker的使用及注意事项
    xml解析
    Intellij Idea2018破解教程(激活到2100年)
    natapp内网映射
    HEAD detached from XXX
    JSON语法
    关于苹果、奔驰、杜蕾斯这些红极一时的品牌
  • 原文地址:https://www.cnblogs.com/histlyb/p/8976635.html
Copyright © 2011-2022 走看看