从零开始学spring源码之xml解析:默认标签解析

Posted doctor灏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始学spring源码之xml解析:默认标签解析相关的知识,希望对你有一定的参考价值。

    上一篇说到spring的默认标签和自定义标签,发现这里面东西还蛮多的。决定还是拆开来写。今天就来好好聊聊这两块是怎么玩的,首先我们先看看默认标签:

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);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

可以看到一共就4个标签:import,alias,bean,beans。 其中最重要的,复杂的,最常用的就是bean标签了,由于篇幅受限(不是因为懒),就只分析bean标签的解析了,其实大抵是相同的,比如import标签其实就是解析完放到一个叫import的map缓存中,alias放到alias的map标签。接下来就详细介绍下bean标签的解析。

点进去看看:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        //解析document,封装成BeanDefinition
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {

            //装饰者设计模式,加上SPI设计思想
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {

                //完成document到BeanDefinition对象转换后,对BeanDefinition对象进行缓存注册
                // 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));
        }
    }

首先看如何封装成beanDefinition对象的:

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        List<String> aliases = new ArrayList<>();
        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.isTraceEnabled()) {
                logger.trace("No XML ‘id‘ specified - using ‘" + beanName +
                        "‘ as bean name and " + aliases + " as aliases");
            }
        }

        //检查beanName是否重复
        if (containingBean == null) {
            checkNameUniqueness(beanName, aliases, ele);
        }

        //通过标签配置解析城beanDefinition对象
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            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.isTraceEnabled()) {
                        logger.trace("Neither XML ‘id‘ nor ‘name‘ specified - " +
                                "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }

主要看这一行:AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

点进去:

public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, @Nullable BeanDefinition containingBean) {

        this.parseState.push(new BeanEntry(beanName));

        String className = null;
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
        String parent = null;
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }

        try {
            //1.创建GenericBeanDefinition对象
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);

            //2.解析bean标签的属性,并把解析出来的属性设置到BeanDefinition对象中
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

            //3.解析bean中的meta标签
            parseMetaElements(ele, bd);

            //4.解析bean中的lookup-method标签  
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

            //5.解析bean中的replaced-method标签  
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

            //6.解析bean中的constructor-arg标签 
            parseConstructorArgElements(ele, bd);

            //7.解析bean中的property标签  
            parsePropertyElements(ele, bd);

            //8.解析bean中的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:

点到里面:

public static AbstractBeanDefinition createBeanDefinition(
            @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setParentName(parentName);
        if (className != null) {
            if (classLoader != null) {
                bd.setBeanClass(ClassUtils.forName(className, classLoader));
            }
            else {
                bd.setBeanClassName(className);
            }
        }
        return bd;
    }

其实就是new了一个GenericBeanDefinition对象,设值。

再看2:其实就是把autowried,primary,factoryMethodName等bean标签属性设置到beanDefinition对象中,

3-8做的基本都一致,只不过解析bean标签不同的属性,这时候beanDefinition对象已经有了,是不是有人觉得已经结束了,

回到bean标签解析刚开始的地方:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        //解析document,封装成BeanDefinition
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {

            //装饰者设计模式,加上SPI设计思想
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {

                //完成document到BeanDefinition对象转换后,对BeanDefinition对象进行缓存注册
                // 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));
        }
    }

是否发现封装beanDefinition对象(其实这时候已经是BeanDefinitionHolder)只是第一步,接下来我们再看看这个所谓的装饰者设计模式和spi思想是个啥玩意(没意义的代码一直往下点就完事了):

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
            Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {

        BeanDefinitionHolder finalDefinition = definitionHolder;

        //根据bean标签属性装饰BeanDefinitionHolder,比如<bean class="xx" p:username="jack"/>
        //
        // Decorate based on custom attributes first.
        NamedNodeMap attributes = ele.getAttributes();
        for (int i = 0; i < attributes.getLength(); i++) {
            Node node = attributes.item(i);
            finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
        }

        //根据bean标签子元素装饰BeanDefinitionHolder
        // Decorate based on custom nested elements.
        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;
    }

这块解析主要分为bean标签元素和子元素,可以看到其实调用的方法是一样的:decorateIfRequired(Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd):

public BeanDefinitionHolder decorateIfRequired(
            Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

        //根据node获取到node的命名空间,形如:http://www.springframework.org/schema/p
        String namespaceUri = getNamespaceURI(node);
        if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {

            //这里有SPI服务发现的思想,根据配置文件获取namespaceUri对应的处理类
            NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
            if (handler != null) {

                //调用NamespaceHandler处理类的decorate方法,开始具体装饰过程,并返回装饰完的对象
                BeanDefinitionHolder decorated =
                        handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
                if (decorated != null) {
                    return decorated;
                }
            }
            else if (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;
    }

首先看一下spring对spi的实现:

public NamespaceHandler resolve(String namespaceUri) {
        //获取spring中所有jar包里面的 "META-INF/spring.handlers"文件,并且建立映射关系
        Map<String, Object> handlerMappings = getHandlerMappings();

        //根据namespaceUri:http://www.springframework.org/schema/p,获取到这个命名空间的处理类
        Object handlerOrClassName = handlerMappings.get(namespaceUri);
        if (handlerOrClassName == null) {
            return null;
        }
        else if (handlerOrClassName instanceof NamespaceHandler) {
            return (NamespaceHandler) handlerOrClassName;
        }
        else {
            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方法,在init方法中完成标签元素解析类的注册
                namespaceHandler.init();
                handlerMappings.put(namespaceUri, namespaceHandler);
                return namespaceHandler;
            }
            catch (ClassNotFoundException ex) {
                throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
                        "] for namespace [" + namespaceUri + "]", ex);
            }
            catch (LinkageError err) {
                throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
                        className + "] for namespace [" + namespaceUri + "]", err);
            }
        }
    }

1.获取所有META-INF/spring.handlers路径下的映射关系,那么这文件里面到底是啥呢:

http://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

不知道大家有没有注意过spring的xml文件上面写的:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:jack="http://www.xiangxueedu.com/schema/mytags"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    http://www.xiangxueedu.com/schema/mytags
    http://www.xiangxueedu.com/schema/mytags.xsd"
    default-lazy-init="false">

有没有发现这就是spring.handlers文件里面的,首先就是用这个作为key来找到后面对应的类路径,找到之后放入缓存(一开始看的时候,没注意缓存名称handlerMappings和新容器名称一致,以为可以作为spring的commiter了,仔细一看,是我想太多了),拿到所有对应spring.handlers文件里面的信息,通过key去取到类路径,然后反射实例化,得到NamespaceHandler处理类实例,调用处理类的init方法,这个方法很重要,比如ContextNamespaceHandler:

public void init() {
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }

有木有发现,我们最最最常用的标签component-scan就是在这里注册的

拿到handler之后,我们需要去开始decorate装饰流程了:

public BeanDefinitionHolder decorate(
            Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
        //拿到节点的处理器类
        BeanDefinitionDecorator decorator = findDecoratorForNode(node, parserContext);
//实际处理
return (decorator != null ? decorator.decorate(node, definition, parserContext) : null); }

看一下获取处理器类的流程:

private BeanDefinitionDecorator findDecoratorForNode(Node node, ParserContext parserContext) {
        BeanDefinitionDecorator decorator = null;
        String localName = parserContext.getDelegate().getLocalName(node);
        if (node instanceof Element) {
            decorator = this.decorators.get(localName);
        }
        else if (node instanceof Attr) {
            decorator = this.attributeDecorators.get(localName);
        }
        else {
            parserContext.getReaderContext().fatal(
                    "Cannot decorate based on Nodes of type [" + node.getClass().getName() + "]", node);
        }
        if (decorator == null) {
            parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionDecorator for " +
                    (node instanceof Element ? "element" : "attribute") + " [" + localName + "]", node);
        }
        return decorator;
    }

看代码逻辑就是从decorators,attributeDecorators这俩缓存中拿的,然后你再回去看一下刚才init方法,其实就是在这一步放入缓存的,比如AopNamespaceHandler:

public void init() {
        // In 2.0 XSD as well as in 2.1 XSD.
        registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
        registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
        registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

        // Only in 2.0 XSD: moved to context namespace as of 2.1
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }
点开registerBeanDefinitionDecorator方法
protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) {
        this.decorators.put(elementName, dec);
    }

剩下的感兴趣的同学可以自己去摸索摸索。

拿到处理类之后,我们就要去做具体的装饰操作,其实就是根据不同的装饰器,基于原有功能添加一些aop拦截之类的附加功能,至此BeanDefinition对象算是完成了。

下一步就是将beanDefinition注册到缓存当中,毕竟后面要用嘛,我们看一下注册的详细逻辑:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        //先判断BeanDefinition是否已经注册
        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (logger.isInfoEnabled()) {
                    logger.info("Overriding user-defined bean definition for bean ‘" + beanName +
                            "‘ with a framework-generated bean definition: replacing [" +
                            existingDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(existingDefinition)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Overriding bean definition for bean ‘" + beanName +
                            "‘ with a different definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    logger.trace("Overriding bean definition for bean ‘" + beanName +
                            "‘ with an equivalent definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                //把beanDefinition缓存到map中
                // Still in startup registration phase
                this.beanDefinitionMap.put(beanName, beanDefinition);

                //把beanName放到beanDefinitionNames list中,这个list着重记住,bean实例化的时候需要用到
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (existingDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

抛开这些校验不看,其实也就是判断缓存是否存在,存在覆盖掉beanDefinitionMap,否则在添加到beanDefinitionMap,同时还要添加一份到beanDefinitionNames中。至此,结束bean标签的解析。

 总结:   

        本来准备把默认标签和自定义标签放在一起写的,写完默认标签发现这个好像写的已经有点长了,还是分两篇写吧。看到这里,其实细心的人应该发现了,到处都是缓存,这些缓存提升了效率,但是我们看起来会很绕,可怜我一个一个去找啊。其实现在想想,没有必要对于每个细节都去了解,时间长了还是会忘,我们看源码,最需要了解的还是思想,思想理解了,后面自己写的时候才能慢慢做到融会贯通。

            

以上是关于从零开始学spring源码之xml解析:默认标签解析的主要内容,如果未能解决你的问题,请参考以下文章

spring源码阅读 Bean加载之默认标签加载

3Spring 源码学习 ~ 默认标签的解析之 Bean 标签解析

Spring源码分析bean标签的解析及注册

4Spring 源码学习 ~ 默认标签的解析之 Bean 标签注册

Spring读源码系列02----默认标签解析过程

6Spring 源码学习 ~ 默认标签的解析之 import 标签的解析