spring xml解析详解

文件DefaultBeanDefinitionDocumentReader.java
在这里插入图片描述首先解析入口是doRegisterBeanDefinitions这个函数。
1.首先创建一个ParseDelegate的类,第一次创建this.delegate没有被赋值为NULL.相当于
this.delegate = createDelegate(getReaderContext(), root, NULL);
  delegate就是 parse的实例对象,后续使用它的内部接口来解析XML,输入bean
2.delegate.initDefaults(root, null );
initDefaults这个函数是初如化一些默认的bean属性,这些属性会从XML读取,如果没有就用默认的。代码截图如下
在这里插入图片描述理解这里的参数很重要,因为大部分xml bean的配置里面可能没有设置相关属性.用的都是默认值,如果不知道默认值是多少就比较麻烦

3.初如化完成后就开始1图的parseBeanDefinitions函数调用开始解析xml了,parseBeanDefinitions内容截图如下
在这里插入图片描述一个xml里面可能有多个Node,parseBeanDefinitions里搞了个循环来处理所有的node.然后就是解析元素了。这里会针对几个元素进行处理
if “import” == NodeName -> importBeanDefinitionResource
if “alias” == NodeName -> processAliasRegistration
if “bean” == NodeName -> processBeanDefinition
if “beans” == NodeName -> doRegisterBeanDefinitions
这里只对processBeanDefinition进行跟踪,看bean的解析过程。
代码如下:

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

//上面两行代码就是读取bean id和bean name ID_ATTRIBUTE=“id” NAME_ATTRIBUTE = "name"
List aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
//上面几行代码大意是:首先看name属性是不是为空,如果不是则解析它。根据代码可知name可以有多个标识符,标识符与标识符之间可以用, ;和空格来分隔。其中MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; "; 然后存放到List数组aliases里面

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

//上面几行代码大体意思是,将读取的id赋值给beanName。如果beanName为空也就是id为空,则读取name属性的第一个标识符同时将其移出LIS**

	if (containingBean == null) {
		checkNameUniqueness(beanName, aliases, ele);
	}

//上面一段代码主要作了一些检查,并将数据COPY到全局变量中去

	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;
}
parseBeanDefinitionElement多态函数调用,截图如下。

在这里插入图片描述1.首先会获取class值CLASS_ATTRIBUTE = “class”  
2 . 再次看它是否有parent属性,有就读取PARENT_ATTRIBUTE = “parent”
3. 根据类名和parent创建一个AbstractBeanDefinition对象。如下图,创建过程比较简单
在这里插入图片描述
在这里插入图片描述从上两幅图可以看出,这个相当简单,就是new了一个GenericBeanDefinition对象,然后把传递过的数据,直接赋值就好了。
4。解析 bean 的各种初始化属性值parseBeanDefinitionAttributes函数截图如下
在这里插入图片描述从上图可以看出这里在尝试从xml配置文件中读取各种bean 对象初始化需要的各种属性。如果没有找到,则使用默认的属性或者不作操作
代码简单明了,不作过多解释。

5。 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
这段代码也很简单,主要是获取描述文字,然后填充成员变量,这里给出一段bean配置的截图就明白了
在这里插入图片描述一般在一个bean里面就只有一个 description属性.而对属性位置没有要求,只要在bean里面就可以。

6。 parseMetaElements(ele, bd);
在这里插入图片描述解析meta 属性META_ELEMENT = “meta”。如果有meta 则读取meta里面的key和value属性。然后用这两个数据生成一个BeanMetadataAttribute对象。然后添加到bd的attributes成员变量里面中。注意AbstractBeanDefinition继承BeanMetadataAttributeAccessor类,所以parseMetaElements传的参数是db.另外meta的保存截图如下

在这里插入图片描述可以看出成员变量attributes是一个map类型,意味着一个bean里面可以拥有多个meta元素。而我们要删除某个attribute时,只需要把value置成空就好了, meta元素使用用例如下
在这里插入图片描述

7。 parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
在这里插入图片描述这里主要是解析LOOKUP_METHOD_ELEMENT = "lookup-method"这个属性值,示例如下在这里插入图片描述
8.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());在这里插入图片描述这里用专门用来解析REPLACED_METHOD_ELEMENT = "replaced-method"这个属性值的,示例如下
在这里插入图片描述9.parseConstructorArgElements(ele, bd);
在这里插入图片描述
这里主要是解析CONSTRUCTOR_ARG_ELEMENT = "constructor-arg"这个属性配置示例如下 :
在这里插入图片描述10。 parsePropertyElements(ele, bd);
在这里插入图片描述
这里主要是解析PROPERTY_ELEMENT = "property"这个属性,先看下示例用法在这里插入图片描述上面的示例是专门找的proxyfactorybean aop的配置。这里重点说下这个解析过程,因为很多自定义的成员内部变量的值注入就是在这里设置的。在这里插入图片描述1)首先会找NAME_ATTRIBUTE = “name” 属性。必须要有不然就是个不合法的配置
2)这里直接把属性名push到LinkedList链表里
3)检查这个属性名是否已经存在,所以属性名要保证唯一性。不然数据不会被加载到内存
4)获取property 属性里面其它的内容,函数截图如下。在这里插入图片描述首先会检查有没有description子元素
然后会检查没有有ref 和 value 属性 。 然后一般情况下ref和value是不应该同时存在的
如果有ref 属性,则返回RuntimeBeanReference对象,可以通过getBeanName获取具体内容。反射注入的时候用到
如果有value属性,则返回TypedStringValue对象。可以通过getValue获取具体内容。反射注入的时候用到
不管返回的是什么对象,我们关心的属性里面的具体内容已经得到。
然后就是解析这个property里面是不是有meta信息了,parseMetaElements(ele, pv);
然后就是保存数据了, bd.getPropertyValues().addPropertyValue(pv); 最终是保存在list里面,看具体实现
在这里插入图片描述
在这里插入图片描述
根据property name字段来判断里面是不是已经存在,如果存在则用新的数据重写,否则添加到list

11 . parseQualifierElements(ele, bd);
在这里插入图片描述
这个是解析QUALIFIER_ELEMENT = “qualifier” 这个属性,示例如下:
在这里插入图片描述到这里差不多就快结束了,最后有两个setSource的调用。
bd.setResource(this.readerContext.getResource()); //这个设置资源文件这个好理解
bd.setSource(extractSource(ele)); //这个调用extractSource函数我找了半天,全是调用栈。干实事的就一个也是醉了。这个好像到最后返回的就是传进去的参数本身。类型强转为Object,不知道理解对不对。

单个bean的配置解析完成了,然后就是注册到hashmap上面去。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bruk_spp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值