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);
        }
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM