您现在的位置是:首页 >其他 >【源码分析】Spring如何解析xml文件生成BeanDefinition网站首页其他

【源码分析】Spring如何解析xml文件生成BeanDefinition

情韵~ 2024-07-13 00:01:02
简介【源码分析】Spring如何解析xml文件生成BeanDefinition

4.1 Spring如何解析xml文件生成BeanDefinition

源码版本:5.3.x | 构建一套本地Spring源码,学习起来真的是太方便了!

从容器启动开始不断Debug,发现Spring中将xml文件的bean的信息转换为BeanDefinition的类为XmlBeanDefinitionReader等一些列的类

下面将直接从核心方法开始进行一步步解析。

XmlBeanDefinitionReader类—>loadBeanDefinitions方法:

将需要进行加载的xml文件转换为流对象信息等,并调用本类的doLoadBeanDefinitions方法

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    // 为空校验
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    // 判断日志是否开启
    if (logger.isTraceEnabled()) {
        logger.trace("Loading XML bean definitions from " + encodedResource);
    }
    // 存储当前要被解析的xml文件资源信息
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    // 判断是否循环加载了该文件
    if (!currentResources.add(encodedResource)) {
        throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }

    try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
        InputSource inputSource = new InputSource(inputStream);
        if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
        }
       	   /**
			 * 获取当前xml文件的流信息,转换为BufferedInputStream流等信息,并进行加载Bean定义信息
			 * inputSource: 当前xml文件的流信息
			 * encodedResource.getResource():当前xml文件的地址信息,以及一些类加载器的信息
			 */
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
    }
    finally {
        currentResources.remove(encodedResource);
        if (currentResources.isEmpty()) {
            this.resourcesCurrentlyBeingLoaded.remove();
        }
    }
}

XmlBeanDefinitionReader类—>doLoadBeanDefinitions方法:

读取xml文件 转换为 org.w3c.dom.Document 对象,继续调用XmlBeanDefinitionReader类的registerBeanDefinitions方法,解析Document对象,注册BeanDefinition

这个方法本身代码比较简单,下面是我把附属的catch捕获的异常删除后的代码:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    throws BeanDefinitionStoreException {
        // 读取xml文件 转换为 org.w3c.dom.Document 对象
        Document doc = doLoadDocument(inputSource, resource);
        // 根据Document对象 解析bean信息。创建beanDefinition信息
        int count = registerBeanDefinitions(doc, resource);
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        return count;
}

XmlBeanDefinitionReader类的registerBeanDefinitions方法:

这段代码比较简单,就是计算一下前后加载了多少beanDifinition

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    // 这里开始解析Document对象,并添加BeanDefinition信息
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

到这里XmlBeanDefinitionReader的职责算是完成了,剩下的便是解析转换后的Document对象

继续跟进这个方法:documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 来到了DefaultBeanDefinitionDocumentReader类中

获取XmlReaderContext对象,解析Document中的节点信息

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    // 获取XmlReaderContext 对象
    this.readerContext = readerContext;
    doRegisterBeanDefinitions(doc.getDocumentElement());
}

继续跟进 doRegisterBeanDefinitions(doc.getDocumentElement());方法:

核心代码:

protected void doRegisterBeanDefinitions(Element root) {
    
    ......
    
    // 这个里面是个空方法,估计是一个标志。。
    preProcessXml(root);
    // 将Document 转换为BeanDefinition
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);

    this.delegate = parent;
}

parseBeanDefinitions(root, this.delegate):真正开始解析xml文件

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		// 判断当前的xml根标签的命名空间是否是自定义的
		if (delegate.isDefaultNamespace(root)) {
			// 获取<beans></beans>标签中所有的子标签
			NodeList nl = root.getChildNodes();
			// 对子标签进行遍历解析
			for (int i = 0; i < nl.getLength(); i++) {
				// 获取子标签每一个节点
				Node node = nl.item(i);
				if (node instanceof Element) {
					/**
					 * Element extends Node ,将节点转换为元素,容易获取内容
					 */
					Element ele = (Element) node;
					// 判断当前元素的xml命名空间是否是自定义的
					if (delegate.isDefaultNamespace(ele)) {
						// 解析当前节点  TODO: 这里开始解析每个标签的元素
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

parseDefaultElement(ele, delegate);:

进行判断要处理的那些标签

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		// 判断当前标签是不是import `IMPORT_ELEMENT` = import
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			// 处理import的标签
			importBeanDefinitionResource(ele);
		}
		// 判断当前标签是不是alias `ALIAS_ELEMENT` = alias
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			// 处理alias的标签
			processAliasRegistration(ele);
		}
		// 判断当前标签是不是bean `BEAN_ELEMENT` = bean
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			// 处理bean标签的信息
			processBeanDefinition(ele, delegate);
		}
		// 这个是比较特殊的 处理 beans标签中的  beans标签
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

这里因为是看bean标签是如何变成BeanDefinition的,所以只看processBeanDefinition(ele, delegate)这个方法

processBeanDefinition(ele, delegate)

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 这里便获取到了bean的定义信息
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // 获取当前标签上的一些属性,然后添加到BeanDefinition里
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // Register the final decorated instance.
            // 将当前bean的定义的信息 注册到BeanDefinitionMap中去
            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));
    }
}

在这里插入图片描述

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele)

这里获取bean的名字,后面还涉及到了生成bean的名字之类的

@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    // 获取id
    String id = ele.getAttribute(ID_ATTRIBUTE);
    // 获取name
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
	// 因为name可以填写多个,所以把name存放在集合里面
    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;
    // 如果没设置id 则会把第一个别名(name)当做beanName
    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");
        }
    }

    if (containingBean == null) {
        checkNameUniqueness(beanName, aliases, ele);
    }
	// 根据beanName 重新生成bean定义信息
    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);
                    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;
}

总结:

Spring将Xml文件转换为Document对象,再对Document对象中的元素进行遍历、解析、分类、存储等。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。