美文网首页
Spring(4) XmlBeanDefinitionRead

Spring(4) XmlBeanDefinitionRead

作者: 百炼 | 来源:发表于2019-08-15 16:35 被阅读0次

    [TOC]
    date [2019-8-15]

    XmlBeanDefinitionReader

    Spring中在外部看来,是通过XMLBeanDefinitionReader来完成Bean定义的加载、解析、以及Bean定义的注册,但是XMLBeanDefinitionReader其底层维护了多个组件来完成上面的每一件事情,这很好地体现了单一职责的设计。而DefaultDocumentLoader这个组件,就是完成Bean定义的加载功能。

    XmlBeanDefinitionReader.png

    XmlBeanDefinitionReader 完成Bean加载

        public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
            BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
            documentReader.setEnvironment(this.getEnvironment());
            int countBefore = getRegistry().getBeanDefinitionCount();
            documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
            return getRegistry().getBeanDefinitionCount() - countBefore;
        }
    

    解析xml生成BeanDefinitionDoucmentReader的类

    DefinitionDocumentReader 只有一个实现类DefaultBeanDefinitionDocumentReader

    BeanDefinitionDocumentReader.png
    package org.springframework.beans.factory.xml;
    
    import java.io.IOException;
    import java.net.URISyntaxException;
    import java.util.LinkedHashSet;
    import java.util.Set;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    
    import org.springframework.beans.factory.BeanDefinitionStoreException;
    import org.springframework.beans.factory.config.BeanDefinitionHolder;
    import org.springframework.beans.factory.parsing.BeanComponentDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
    import org.springframework.core.env.Environment;
    import org.springframework.core.io.Resource;
    import org.springframework.core.io.support.ResourcePatternUtils;
    import org.springframework.util.Assert;
    import org.springframework.util.ResourceUtils;
    import org.springframework.util.StringUtils;
    public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
    
        public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;
    
        public static final String NESTED_BEANS_ELEMENT = "beans";
    
        public static final String ALIAS_ELEMENT = "alias";
    
        public static final String NAME_ATTRIBUTE = "name";
    
        public static final String ALIAS_ATTRIBUTE = "alias";
    
        public static final String IMPORT_ELEMENT = "import";
    
        public static final String RESOURCE_ATTRIBUTE = "resource";
    
        public static final String PROFILE_ATTRIBUTE = "profile";
    
    
        protected final Log logger = LogFactory.getLog(getClass());
    
        private Environment environment;
    
        private XmlReaderContext readerContext;
    
        private BeanDefinitionParserDelegate delegate;
    
        /**
         * {@inheritDoc}
         * <p>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.
         */
        public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
            this.readerContext = readerContext;
            logger.debug("Loading bean definitions");
            Element root = doc.getDocumentElement();
            doRegisterBeanDefinitions(root);
        }
    
    
        /**
         * Register each bean definition within the given root {@code <beans/>} element.
         * @throws IllegalStateException if {@code <beans profile="..."} attribute is present
         * and Environment property has not been set
         * @see #setEnvironment
         */
        protected void doRegisterBeanDefinitions(Element root) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!this.environment.acceptsProfiles(specifiedProfiles)) {
                    return;
                }
            }
    
            // 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(this.readerContext, root, parent);
    
            preProcessXml(root);
            parseBeanDefinitions(root, this.delegate);
            postProcessXml(root);
    
            this.delegate = parent;
        }
    
        protected BeanDefinitionParserDelegate createDelegate(
                XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
    
            BeanDefinitionParserDelegate delegate = createHelper(readerContext, root, parentDelegate);
            if (delegate == null) {
                delegate = new BeanDefinitionParserDelegate(readerContext, this.environment);
                delegate.initDefaults(root, parentDelegate);
            }
            return delegate;
        }
    
        @Deprecated
        protected BeanDefinitionParserDelegate createHelper(
                XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
    
            return null;
        }
    
        /**
         * Return the descriptor for the XML resource that this parser works on.
         */
        protected final XmlReaderContext getReaderContext() {
            return this.readerContext;
        }
    
        /**
         * Invoke the {@link org.springframework.beans.factory.parsing.SourceExtractor} to pull the
         * source metadata from the supplied {@link Element}.
         */
        protected Object extractSource(Element ele) {
            return this.readerContext.extractSource(ele);
        }
    
        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)) {
                            parseDefaultElement(ele, delegate);
                        }
                        else {
                            delegate.parseCustomElement(ele);
                        }
                    }
                }
            }
            else {
                delegate.parseCustomElement(root);
            }
        }
    
        private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
            if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
                importBeanDefinitionResource(ele);
            }
            else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
                processAliasRegistration(ele);
            }
            else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
                processBeanDefinition(ele, delegate);
            }
            else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
                // recurse
                doRegisterBeanDefinitions(ele);
            }
        }
        protected void importBeanDefinitionResource(Element ele) {
            String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
            if (!StringUtils.hasText(location)) {
                getReaderContext().error("Resource location must not be empty", ele);
                return;
            }
            // Resolve system properties: e.g. "${user.dir}"
            location = environment.resolveRequiredPlaceholders(location);
    
            Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
    
            // Discover whether the location is an absolute or relative URI
            boolean absoluteLocation = false;
            try {
                absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
            }
            catch (URISyntaxException ex) {
                // cannot convert to an URI, considering the location relative
                // unless it is the well-known Spring prefix "classpath*:"
            }
    
            // Absolute or relative?
            if (absoluteLocation) {
                try {
                    int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
                    }
                }
                catch (BeanDefinitionStoreException ex) {
                    getReaderContext().error(
                            "Failed to import bean definitions from URL location [" + location + "]", ele, ex);
                }
            }
            else {
                // No URL -> considering resource location as relative to the current file.
                try {
                    int importCount;
                    Resource relativeResource = getReaderContext().getResource().createRelative(location);
                    if (relativeResource.exists()) {
                        importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                        actualResources.add(relativeResource);
                    }
                    else {
                        String baseLocation = getReaderContext().getResource().getURL().toString();
                        importCount = getReaderContext().getReader().loadBeanDefinitions(
                                StringUtils.applyRelativePath(baseLocation, location), actualResources);
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
                    }
                }
                catch (IOException ex) {
                    getReaderContext().error("Failed to resolve current resource location", ele, ex);
                }
                catch (BeanDefinitionStoreException ex) {
                    getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
                            ele, ex);
                }
            }
            Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
            getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
        }
        protected void processAliasRegistration(Element ele) {
            String name = ele.getAttribute(NAME_ATTRIBUTE);
            String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
            boolean valid = true;
            if (!StringUtils.hasText(name)) {
                getReaderContext().error("Name must not be empty", ele);
                valid = false;
            }
            if (!StringUtils.hasText(alias)) {
                getReaderContext().error("Alias must not be empty", ele);
                valid = false;
            }
            if (valid) {
                try {
                    getReaderContext().getRegistry().registerAlias(name, alias);
                }
                catch (Exception ex) {
                    getReaderContext().error("Failed to register alias '" + alias +
                            "' for bean with name '" + name + "'", ele, ex);
                }
                getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
            }
        }
        protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
            BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
            if (bdHolder != null) {
                bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                try {
                    // Register the final decorated instance.
                    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
                }
                catch (BeanDefinitionStoreException ex) {
                    getReaderContext().error("Failed to register bean definition with name '" +
                            bdHolder.getBeanName() + "'", ele, ex);
                }
                // Send registration event.
                getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
            }
        }
        protected void preProcessXml(Element root) {
        }
        protected void postProcessXml(Element root) {
        }
    }
    

    EntityResolver

    在loadDocument方法中涉及一个参数EntityResolver。
    EntityResolver的作用是项目本身就可以提供一个如何寻找DTD声明的方法,由程序来实现寻找DTD声明的过程,比如将DTD文件放到
    项目某处,在实现时直接将此文档读取并返回给SAX。避免通过网络来寻找相应的声明。
    EntityResolver接口声明如下:

    public interface EntityResolver {
        public abstract InputSource resolveEntity (String publicId,
                                                   String systemId)
            throws SAXException, IOException;
    }
    

    相关文章

      网友评论

          本文标题:Spring(4) XmlBeanDefinitionRead

          本文链接:https://www.haomeiwen.com/subject/xgwzjctx.html