2. Xml 标签解析

 

前面讲到,Xml中的标签分为两种,一种是Spring默认标签,例如 <bean id="bookService" class="com.newcoder.api.BookService">,另一种是自定义标签,例如 <mvc:annotation-driven />,这一节,我们就来看看默认标签和自定义标签的解析方法。

一、默认标签解析

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        // import标签解析
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        // Alias标签解析
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        // bean标签解析
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        // beans标签解析,使其作为root,循环调用文件解析(回忆一下,整个applicationContext.xml本身不就是一个beans么?)
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

1. Bean标签解析

1. 元素解析

2. 如果默认标签下存在自定义标签,则需要对其进行解析

3. 对解析后的bdHolder进行注册

4. bean加载完成,通知相关监听器

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        // 1. 元素解析
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            // 2. 如果默认标签下存在自定义标签,则需要对其进行解析
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 3. 对解析后的bdHolder进行注册
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // 4. bean加载完成,通知相关监听器
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

   1.1. 元素解析

1. 解析 id, name 属性

2.  校验 name 的重复性

3. 创建 BeanDefinition

  3.1 创建一个BeanDefinition(该对象上属性与bean标签中属性一一对应)

  3.2 解析所有属性并将其赋值在BeanDefinition上

4. 如果 beanName 为空,则生成默认的 beanName

5. 将 BeanDefinition 封装进 BeanDefinitionHolder 中返回

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
    }
    
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        // 获取id
        String id = ele.getAttribute(ID_ATTRIBUTE);
        // 获取name(如果想用多个别名,使用逗号或分号连接)
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        List<String> aliases = new ArrayList<String>();
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
            if (logger.isDebugEnabled()) {
                logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
            }
        }

        // bean名称不能重复
        if (containingBean == null) {
            checkNameUniqueness(beanName, aliases, ele);
        }
        
        // 创建BeanDefinition
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            // 如果没有指定beanName,则生成beanName
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        // Register an alias for the plain bean class name, if still possible,
                        // if the generator returned the class name plus a suffix.
                        // This is expected for Spring 1.2/2.0 backwards compatibility.
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&
                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Neither XML 'id' nor 'name' specified - " +
                                "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            // 将BeanDefinition封装进BeanDefinitionHolder中
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

  下面来分析下 BeanDefinition 的创建过程:

    public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, BeanDefinition containingBean) {
        // 1. 使用Stack存储
        this.parseState.push(new BeanEntry(beanName));
        
        // 2. 解析class、parent属性
        // 2.1 解析class属性
        String className = null;
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }

        try {
            // 2.2 解析parent属性
            String parent = null;
            if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                parent = ele.getAttribute(PARENT_ATTRIBUTE);
            }
            
            // 3. 创建GenericBeanDefinition对象,将class信息放进去
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            
            // 4. 解析Bean各种属性,赋到BeanDefinition中
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            // 5. 提取description
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
            
            // 6. 解析子元素meta
            parseMetaElements(ele, bd);
            // 7. 解析子元素lookup-method
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            // 8. 解析子元素replaced-method
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            // 9. 解析子元素constructor-arg
            parseConstructorArgElements(ele, bd);
            // 10. 解析子元素property
            parsePropertyElements(ele, bd);
            // 11. 解析子元素qualifier
            parseQualifierElements(ele, bd);

            bd.setResource(this.readerContext.getResource());
            bd.setSource(extractSource(ele));

            return bd;
        }
        catch (ClassNotFoundException ex) {
            error("Bean class [" + className + "] not found", ele, ex);
        }
        catch (NoClassDefFoundError err) {
            error("Class that bean class [" + className + "] depends on not found", ele, err);
        }
        catch (Throwable ex) {
            error("Unexpected failure during bean definition parsing", ele, ex);
        }
        finally {
            this.parseState.pop();
        }

        return null;
    }

  1.2 解析默认标签下的自定义标签

1. 循环遍历属性,如果有自定义标签,则解析

2. 循环遍历子节点,如果有自定义标签,则解析

    public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
        return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
    }
    
    // containingBd:父类的BeanDefinition,如果子类没有设置scope,默认使用父类的scope
    public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
            Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {

        BeanDefinitionHolder finalDefinition = definitionHolder;

        // 对属性中的自定义标签进行解析
        NamedNodeMap attributes = ele.getAttributes();
        for (int i = 0; i < attributes.getLength(); i++) {
            Node node = attributes.item(i);
            finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
        }

        // 对子节点中的自定义标签进行解析
        NodeList children = ele.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node node = children.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
            }
        }
        return finalDefinition;
    }
    
    public BeanDefinitionHolder decorateIfRequired(
            Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {

        String namespaceUri = getNamespaceURI(node);
        // 如果不是默认标签,则使用NamespaceHandler进行解析(自定义标签解析)
        if (!isDefaultNamespace(namespaceUri)) {
            NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
            if (handler != null) {
                return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
            }
            else if (namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) {
                error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
            }
            else {
                // A custom namespace, not to be handled by Spring - maybe "xml:...".
                if (logger.isDebugEnabled()) {
                    logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
                }
            }
        }
        return originalDef;
    }

  1.3 对解析后的 bdHolder 进行注册

  这里的注册分为两部分,第一部分是beanName与BeanDefinition的注册,用于通过beanName找到BeanDefinition;第二部分是aliases与beanName的注册,将所有的别名绑定到beanName上。

    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // 通过beanName进行注册,<beanName, BeanDefinition>
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // 通过别名进行注册 <alais, beanName>
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

  1.4 bean加载完成,通知相关监听器

  当BeanDefinition注册完成之后,调用监听此事件的程序。Spring在这里的实现是空的,开发人员可以自己实现扩展。

    public void fireComponentRegistered(ComponentDefinition componentDefinition) {
        this.eventListener.componentRegistered(componentDefinition);
    }

2. import标签解析

1. 获取 import 标签中的 resource 属性

2. 替换掉 location 中的系统参数

3. 判断 location 是绝对路径还是相对路径

  3.1 绝对路径,直接读取,然后递归调用xml文件解析

  3.2 相对路径,先转换为绝对路径再读取,然后递归调用文件xml解析

4. 解析完成,通知相关监听器

    protected void importBeanDefinitionResource(Element ele) {
        // 1. 解析 resource 属性,拿到文件地址
        String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
        if (!StringUtils.hasText(location)) {
            getReaderContext().error("Resource location must not be empty", ele);
            return;
        }

        // 对文件地址中的系统参数进行替换,例如:"${user.dir}"
        location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

        Set<Resource> actualResources = new LinkedHashSet<Resource>(4);

        // 判断location是 绝对路径 还是 相对路径
        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*:"
        }

        // 如果是绝对路径,直接根据地址加载对应的配置文件
        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 {
            // 如果是相对路径,则根据相对地址计算出绝对地址
            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));
    }

3. Alias标签解析

1. 属性解析:beanName、alias

2. 建立别名映射

    protected void processAliasRegistration(Element ele) {
        // 获取beanName
        String name = ele.getAttribute(NAME_ATTRIBUTE);
        // 获取alias
        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 {
                // 注册alias到beanName的映射,<alias, beanName>
                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));
        }
    }

4. beans标签解析

  看,我们一直解析的就是 beans 啊!

  那么如果在文件里再遇到 beans 怎么办,就把它当作单独的配置文件解析不就完了么。

 

 

二、自定义标签解析

   自定义标签的定义及使用请移步到 基于Spring可扩展Schema提供自定义配置支持(spring配置文件中 配置标签支持)

 自定义标签解析分为三个过程:

1. 获取节点的命名空间

2. 根据命名空间找到 NamespaceHandler

3. 通过 NamespaceHandler 解析节点

1. 获取节点命名空间

  这里不用多说,命名空间就是节点的uri。

    public String getNamespaceURI(Node node) {
        return node.getNamespaceURI();
    }

2. 根据命名空间找到 NamespaceHandler

1. 获取所有已配置的handlers映射(第一次从 Spring.handlers 中加载,存在map中)

2. 如果通过 handlerMappings 直接可以获得 Namespacehandler 对象,则直接返回

3. 首次加载NamespaceHandler,实例化对象后放入handlerMappings中,覆盖掉之前的class全路径,之后就不用再创建了

  3.1 调用自定义的 init 方法,用于注册 BeanDefinitionParser 解析器。

    public NamespaceHandler resolve(String namespaceUri) {
        // 1. 获取所有已配置的handler映射(第一次从Spring.handlers中加载,然后存在map中)
        Map<String, Object> handlerMappings = getHandlerMappings();
        Object handlerOrClassName = handlerMappings.get(namespaceUri);
        if (handlerOrClassName == null) {
            return null;
        }
        // 2. 如果获取到NamespaceHandler,直接返回(首次获取到NamespaceHandler后会将其放进handlerMappings中)
        else if (handlerOrClassName instanceof NamespaceHandler) {
            return (NamespaceHandler) handlerOrClassName;
        }
        else {
            // 3. 首次加载NamespaceHandler,实例化对象后放入handlerMappings中,覆盖掉之前的class全路径,之后就不用再创建了
            String className = (String) handlerOrClassName;
            try {
                Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
                if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                    throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                            "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
                }
                NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
                // 调用自定义的init方法,也就是registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser),用来绑定BeanDefinitionParser解析器
                namespaceHandler.init();
                handlerMappings.put(namespaceUri, namespaceHandler);
                return namespaceHandler;
            }
            catch (ClassNotFoundException ex) {
                throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
                        namespaceUri + "] not found", ex);
            }
            catch (LinkageError err) {
                throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
                        namespaceUri + "]: problem with handler class file or dependent class", err);
            }
        }
    }

3. 通过 Namespace 进行解析

  我们看 parse(..) 方法,这里分为两步,第一是根据节点找到对应的解析器,第二是使用该解析器解析该节点。

    public BeanDefinition parse(Element element, ParserContext parserContext) {
        // 1. 寻找解析器
        // 2. 解析该节点
        return findParserForElement(element, parserContext).parse(element, parserContext);
    }

  3.1 寻找节点对应的解析器

  首先是获取元素名称,这里的名称是指 <context:component-scan> 中的 component-scan。

  然后从之前的注册器(Map) 中获取parser。

    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
        // 获取元素名称
        String localName = parserContext.getDelegate().getLocalName(element);
        // 根据元素名称获取解析器(还记得之前在init方法中注册了解析器么?)
        BeanDefinitionParser parser = this.parsers.get(localName);
        if (parser == null) {
            parserContext.getReaderContext().fatal(
                    "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
        }
        return parser;
    }

  3.2 使用解析器进行解析

1. 标签解析并转换为 BeanDefinition

  1.1 使用 Builder 模式,创建 BeanDefinitionBuilder

  1.2 通过自定义解析器的覆写方法获取 beanClass 或 beanClassName

  1.3 如果存在父类,则使用父类的 scope

  1.4 如果父类时懒加载,那么它也是懒加载

  1.5 调用自定义解析器腹泻的 doParse(..) 方法完成解析

  1.6 使用 Builder 模式创建 BeanDefinition

2. 获取别名

3. 创建 BeanDefinitionHolder 并注册

4. 通知相关监听器

public final BeanDefinition parse(Element element, ParserContext parserContext) {
        // 1. 标签解析并转换为BeanDefinition
        AbstractBeanDefinition definition = parseInternal(element, parserContext);
        if (definition != null && !parserContext.isNested()) {
            try {
                String id = resolveId(element, definition, parserContext);
                if (!StringUtils.hasText(id)) {
                    parserContext.getReaderContext().error(
                            "Id is required for element '" + parserContext.getDelegate().getLocalName(element)
                                    + "' when used as a top-level tag", element);
                }
                
                // 2. 获取别名(这里的shouldParseNameAsAliases()固定返回true)
                String[] aliases = null;
                if (shouldParseNameAsAliases()) {
                    String name = element.getAttribute(NAME_ATTRIBUTE);
                    if (StringUtils.hasLength(name)) {
                        aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
                    }
                }
                // 3. 创建BeanDefinitionHolder并注册,这里的id对应beanName
                BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
                registerBeanDefinition(holder, parserContext.getRegistry());
                // 4. 通知相关监听器(shouldFireEvents()固定返回true)
                if (shouldFireEvents()) {
                    BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
                    // 解析后处理,自己扩展实现
                    postProcessComponentDefinition(componentDefinition);
                    parserContext.registerComponent(componentDefinition);
                }
            }
            catch (BeanDefinitionStoreException ex) {
                parserContext.getReaderContext().error(ex.getMessage(), element);
                return null;
            }
        }
        return definition;
    }
    
    protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        // 创建BeanDefinition的Builder(Builder模式)
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        String parentName = getParentName(element);
        if (parentName != null) {
            builder.getRawBeanDefinition().setParentName(parentName);
        }
        
        // 获取class属性,调用自定义解析器中覆盖的 getBeanClass(..)方法
        Class<?> beanClass = getBeanClass(element);
        if (beanClass != null) {
            builder.getRawBeanDefinition().setBeanClass(beanClass);
        }
        else {
            // 如果自定义解析器没有覆盖getBeanClass(..)方法,则检查有没有覆盖getBeanClassName(..)方法
            String beanClassName = getBeanClassName(element);
            if (beanClassName != null) {
                builder.getRawBeanDefinition().setBeanClassName(beanClassName);
            }
        }
        builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
        // 如果存在父类,则使用父类的scope
        if (parserContext.isNested()) {
            builder.setScope(parserContext.getContainingBeanDefinition().getScope());
        }
        // 如果父类是懒加载,那么它也是懒加载
        if (parserContext.isDefaultLazyInit()) {
            builder.setLazyInit(true);
        }
        // 调用自定义解析器中的doParser(..)方法进行解析
        doParse(element, parserContext, builder);
        // 使用Builder模式创建BeanDefinition
        return builder.getBeanDefinition();
    }

 

posted @ 2019-10-23 17:58  莹狼  阅读(576)  评论(0编辑  收藏  举报