栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring源码系列:标签的解析原理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring源码系列:标签的解析原理

Spring源码系列:标签的解析原理

前言一. 默认标签的解析

1.1 bean标签的解析和注册

1.1.1 解析BeanDefinition

(1) GenericBeanDefinition对象(2) 解析各种属性(3) lookup-method标签的作用(4) replaced-method标签的作用(5) constructor-arg标签的作用(6) 解析property子元素(7) qualifier子元素 1.1.2 AbstractBeanDefinition的属性1.1.3 解析默认标签中的自定义标签1.1.3 注册解析的BeanDefinition

(1) 通过beanName注册(2) 通过别名注册 二. 自定义标签的解析

2.1 自定义标签的运用2.2 自定义标签解析原理

2.2.1 提取自定义标签处理器2.2.2 标签解析 三. 大总结☆

前言

在了解了Spring容器加载资源的一个过程后,其对资源加载的最后一步则是生成对应的BeanDefinition,在本篇文章,我们着重讲Spring是如何对配置文件的标签进行解析的流程,parseDefaultElement和parseCustomElement方法。

先从parseDefaultElement开始:

一. 默认标签的解析

默认标签的解析,会分别对4种不同的标签做处理:

import:importBeanDefinitionResource()alias:processAliasRegistration()bean:processBeanDefinition()beans:doRegisterBeanDefinitions()

public class DefaultBeanDefinitiondocumentReader implements BeanDefinitiondocumentReader {
	// 这里的ele对象,是Spring容器已经将配置资源读取、加载完成并转化为document对象了,可以理解为文档对象的节点。
	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);
		}
	}
}

还记得我们上篇文章中讲的小案例吗?里面用的就是bean标签,这也是最基本最常用的一个标签,因此我们着重来讲它。



   	
		
		
	

1.1 bean标签的解析和注册

让我们来深入processBeanDefinition方法:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	// 1.进行Bean元素解析,返回的实例bdHolder中,已经包含配置文件里面的各种属性了,例如:class、name、id、alias等。
	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) {
			// ...
		}
		// 4.发送响应事件,通知相关监听器,该bean已经加载完成了。
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}
1.1.1 解析BeanDefinition

这一小节,我们主要关注这行代码BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

public class BeanDefinitionParserDelegate {
	@Nullable
	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
		// 解析id属性和name属性
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
		// 1.分割name属性,根据 ,; 来分割,MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";
		List 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);
			// log...
		}
	
		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}
		// 2.这里会进一步解析其他所有属性,并统一封装成beanDefinition,最后返回的实例对象为GenericBeanDefinition类型,
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
					// 3.若不存在beanName,则根据Spring的命名规则为当前bean生成对应的beanName
					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);
						}
					}
					// log...
				}
				catch (Exception ex) {
					error(ex.getMessage(), ele);
					return null;
				}
			}
			String[] aliasesArray = StringUtils.toStringArray(aliases);
			// 4.将获取到的信息封装成BeanDefinitionHolder的实例中并返回。
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}
		return null;
	}
}

简而言之就是:

    解析id和name属性,并对name属性进行分割。解析其他属性。若beanName没有,则自动生成个。将上述获取到结果的再进行整合,得到最终的BeanDefinitionHolder实例对象。

那我们再来细看下第二步的 “解析其他属性” 的过程:
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

形式上就像根据不同的Key,来获取不同的Value而已value = getAttribute(key)

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

	this.parseState.push(new BeanEntry(beanName));
	// 解析class属性
	String className = null;
	if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
		className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
	}
	// 解析parent属性
	String parent = null;
	if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
		parent = ele.getAttribute(PARENT_ATTRIBUTE);
	}

	try {
		// 创建一个用于承载属性的GenericBeanDefinition对象
		AbstractBeanDefinition bd = createBeanDefinition(className, parent);
		// 硬编码解析默认bean的各种属性
		parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
		// 提取description,bean的一个描述信息
		bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DEscriptION_ELEMENT));
		// 解析元数据
		parsemetaElements(ele, bd);
		// 解析lookup-method属性:把一个方法声明为返回某种类型的bean的标签。用于设计插拔功能
		parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
		// 解析replaced-method属性:可以在运行时用新的方法替换旧的方法
		parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
		// 解析构造函数参数
		parseConstructorArgElements(ele, bd);
		// 解析property子元素
		parsePropertyElements(ele, bd);
		// 解析qualifier子元素
		parseQualifierElements(ele, bd);

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

		return bd;
	}
	catch {
		// 各种catch...
	}
	finally {
		this.parseState.pop();
	}

	return null;
}

这里的解析其他属性,也就是解析class、description、property等属性。我们先来看下,用于承载这些属性的GenericBeanDefinition对象:

(1) GenericBeanDefinition对象

BeanDefinition是一个接口,他是配置文件元素标签在容器内部的一个表现形式。 例如:标签拥有class、scope、lazy-init等配置属性。而BeanDefinition则提供了对应的beanClass、scope、lazyInit操作方法。其为一一对应的一个关系。

Spring中,BeanDefinition的实现有这3个常见的,它们都继承了AbstractBeanDefinition

RootBeanDefinition:最常用的实现类,对应一般性的元素标签。ChildBeanDefinition:用于标识子标签。GenericBeanDefinition:一种一站式服务类

Spring容器一般在读取加载完配置资源文件后,将其转为BeanDefinition类型实例的内部表示,而这些实例一般会注册到BeanDefinitionRegistry中。其主要以map形式来保存,后续操作都是从BeanDefinitionRegistry中读取配置信息的。

(2) 解析各种属性

我们来看下parseBeanDefinitionAttributes()这个方法:

建议:如果想知道里面解析了哪些具体的属性,读者可以展开代码瞅瞅,否则,这段代码其实没什么好看的。

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
		@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
	// 1.解析scope属性。singleton这个标签已经过时了,改用scope来声明
	if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
		error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
	}else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
		bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
	}else if (containingBean != null) {
		bd.setScope(containingBean.getScope());
	}
	// 2.解析abstract属性
	if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
		bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
	}
	// 3.解析lazy-init属性
	String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
	if (isDefaultValue(lazyInit)) {
		lazyInit = this.defaults.getLazyInit();
	}
	bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
	// 4.解析autowire属性
	String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
	bd.setAutowireMode(getAutowireMode(autowire));
	// 5.解析depends-on属性
	if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
		String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
		bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
	}
	// 6.解析autowire-candidate属性
	String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
	if (isDefaultValue(autowireCandidate)) {
		String candidatePattern = this.defaults.getAutowireCandidates();
		if (candidatePattern != null) {
			String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
			bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
		}
	}else {
		bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
	}
	// 7.解析primary属性
	if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
		bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
	}
	// 8.解析init-method属性
	if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
		String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
		bd.setInitMethodName(initMethodName);
	}else if (this.defaults.getInitMethod() != null) {
		bd.setInitMethodName(this.defaults.getInitMethod());
		bd.setEnforceInitMethod(false);
	}
	// 9.解析destroy-method属性
	if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
		String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
		bd.setDestroyMethodName(destroyMethodName);
	}else if (this.defaults.getDestroyMethod() != null) {
		bd.setDestroyMethodName(this.defaults.getDestroyMethod());
		bd.setEnforceDestroyMethod(false);
	}
	// 10.解析factory-method属性
	if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
		bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
	}
	// 11.解析factory-bean 属性
	if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
		bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
	}

	return bd;
}
(3) lookup-method标签的作用

案例如下:

项目结构

User类:

public class User {
	void getName(){
		System.out.println("I am User");
	}
}

Student类:

public class Student extends User{
	void getName(){
		System.out.println("I am Student");
	}
}

GetNameTest类:

public abstract class GetNameTest {
	public abstract User getBean();

	public void getName() {
		// 调用了抽象方法getBean(),返回User实例
		this.getBean().getName();
	}
}

test.xml文件:



   	
	
		
	
	

Test类:

public class Test {
	public static void main(String[] args) {
		ClassPathResource resource = new ClassPathResource("test.xml");
		DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
		reader.loadBeanDefinitions(resource);

		GetNameTest getNameTest = (GetNameTest) factory.getBean("getNameTest");
		getNameTest.getName();
	}
}

结果如下:

原理分析:

parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
代码展开如下:
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		// 仅仅在 Spring默认bean的子元素为lookup-method 时有效
		if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
			Element ele = (Element) node;
			// 获取要修饰的方法
			String methodName = ele.getAttribute(NAME_ATTRIBUTE);
			// 获取配置返回的bean引用
			String beanRef = ele.getAttribute(BEAN_ELEMENT);
			// LookupOverride实体对象用于记录属性 ,最终记录在AbstractBeanDefinition类中的methodOverrides中
			LookupOverride override = new LookupOverride(methodName, beanRef);
			override.setSource(extractSource(ele));
			overrides.addOverride(override);
		}
	}
}

也就是Spring会读取特定的标签,并根据配置的bean的具体的引用来执行对应的方法。代码中,getBean()并没有具体的实现,但是我们通过XML配置,将该抽象方法声明为返回了某种类型的bean,在本案例中就是返回了Student对象实例。

再把两者代码结合在一起来看看:

所以,当我们在业务中,不再需要Student类的相关逻辑了,需要Teacher类的getName()方法,我们只需要换一个引用即可,该标签常用于插拔功能。

(4) replaced-method标签的作用

案例如下:
GetNameTest类:

public class GetNameTest {
	public void getName() {
		System.out.println("Hello");
	}
}

Change类:需要实现MethodReplacer接口

import org.springframework.beans.factory.support.MethodReplacer;

import java.lang.reflect.Method;

public class Change implements MethodReplacer {
	@Override
	public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
		System.out.println("Method Change!");
		return null;
	}
}

test.xml:




	
		
	
	

Test类:

public class Test {
	public static void main(String[] args) {
		ClassPathResource resource = new ClassPathResource("test.xml");
		DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
		reader.loadBeanDefinitions(resource);

		GetNameTest getNameTest = (GetNameTest) factory.getBean("getNameTest");
		getNameTest.getName();
	}
}

结果如下:

可以看出来,标签允许我们在运行时用新方法代替现有的方法。

那么上述两种标签有什么区别呢?

:一般用于替代现有逻辑。:一般用于执行其他方案,需要创建其他Bean。

源码分析:(建议结合XML配置来看)

public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		// 仅当在默认bean的子标签为replace-method情况下有效
		if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
			Element replacedMethodEle = (Element) node;
			// 提取要替换的旧方法
			String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
			// 提取对应新的替换方法
			String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
			ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
			// Look for arg-type match elements.
			List argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
			for (Element argTypeEle : argTypeEles) {
				// 记录其参数
				String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
				match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
				if (StringUtils.hasText(match)) {
					replaceOverride.addTypeIdentifier(match);
				}
			}
			replaceOverride.setSource(extractSource(replacedMethodEle));
			overrides.addOverride(replaceOverride);
		}
	}
}

注意:

两种标签,最后都是创建了MethodOverride实例:LookupOverride与ReplaceOverride。用于记录相关参数。而其最终都是记录在AbstractBeanDefinition类中的methodOverrides属性中。 (5) constructor-arg标签的作用

首先,我们先来看下这个标签有什么作用。案例如下:
User类:

public class User {
	private int id;
	private String name;
	private Map map;

	public User(int id, String name, Map map) {
		this.id = id;
		this.name = name;
		this.map = map;
	}
	
	public Map getMap() {
		return map;
	}

	public void setMap(Map map) {
		this.map = map;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Test方法:

@org.junit.jupiter.api.Test
public void test() {
	BeanFactory factory = new DefaultListableBeanFactory();
	XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) factory);

	ClassPathResource resource = new ClassPathResource("user.xml");
	reader.loadBeanDefinitions(resource);

	User user = (User) factory.getBean("user");
	System.out.println(user.getId());
	System.out.println(user.getName());
	System.out.println(user.getMap().get("key"));
}

user.xml:



	
		
			1
		
		
			名字
		
		
			
				
			
		
	

结果如下:

可以看到,该标签在功能上,实现了初始化Bean的时候,将设置的参数传了进去。那么我们再来看下Spring默认解析标签时对该标签解析的调用起点位置:

parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
↓↓↓↓↓↓
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
	NodeList nl = beanEle.getChildNodes();
	// 遍历所有子元素,即提取所有构造函数的参数并解析
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
			// 具体的解析函数
			parseConstructorArgElement((Element) node, bd);
		}
	}
}
↓↓↓↓↓↓parseConstructorArgElement↓↓↓↓↓↓
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
	// 提取index属性,对应构造函数的第几个参数
	String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
	// 提取type属性
	String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
	// 提取name属性
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
	if (StringUtils.hasLength(indexAttr)) {
		try {
			// 构造函数的参数下标,不可能比0小
			int index = Integer.parseInt(indexAttr);
			if (index < 0) {
				error("'index' cannot be lower than 0", ele);
			}
			else {
				try {
					this.parseState.push(new ConstructorArgumentEntry(index));
					// 解析ele对应的属性元素
					Object value = parsePropertyValue(ele, bd, null);
					ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
					if (StringUtils.hasLength(typeAttr)) {
						valueHolder.setType(typeAttr);
					}
					if (StringUtils.hasLength(nameAttr)) {
						valueHolder.setName(nameAttr);
					}
					valueHolder.setSource(extractSource(ele));
					//不允许重复指定相同的参数
					if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
						error("Ambiguous constructor-arg entries for index " + index, ele);
					}
					else {
						// 将type name index属性都封装进去,并将最终的封装实例塞到BeanDefinition的ConstructorArgumentValues下的indexedArgumentValues属性中
						bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
					}
				}
				finally {
					this.parseState.pop();
				}
			}
		}
		catch (NumberFormatException ex) {
			error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
		}
	}
	else {
		// 若没有指定index属性,则自动寻找
		try {
			this.parseState.push(new ConstructorArgumentEntry());
			Object value = parsePropertyValue(ele, bd, null);
			// valueHolder用于封装解析出来的元素
			ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
			// 将type name index属性都封装进去,并将最终的封装实例,塞到BeanDefinition的ConstructorArgumentValues下的genericArgumentValues属性中
			if (StringUtils.hasLength(typeAttr)) {
				valueHolder.setType(typeAttr);
			}
			if (StringUtils.hasLength(nameAttr)) {
				valueHolder.setName(nameAttr);
			}
			valueHolder.setSource(extractSource(ele));
			bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
		}
		finally {
			this.parseState.pop();
		}
	}
}

上述流程当中,分为两种情况:

情况1:配置中指定了index属性:情况2:配置中没有指定index属性。

两种情况的前几步骤是一样的:

    解析Constructor-arg的子元素。使用valueHolder实例去封装解析出来的元素。

唯一不同的就是:

情况1:将valueHolder实例封装于BeanDefinition下的indexedArgumentValues属性中。情况2:将valueHolder实例封装于BeanDefinition下的genericArgumentValues属性中。


在此之前,先来讲一下解析构造子元素的方法原理parsePropertyValue:

Object value = parsePropertyValue(ele, bd, null);
↓↓↓↓↓↓
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
	String elementName = (propertyName != null ?
			" element for property '" + propertyName + "'" :
			" element");

	// 一个属性只能对应一种类型:ref, value, list, etc的一种
	NodeList nl = ele.getChildNodes();
	Element subElement = null;
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		//对应description或者meta则不处理
		if (node instanceof Element && !nodeNameEquals(node, DEscriptION_ELEMENT) &&
				!nodeNameEquals(node, meta_ELEMENT)) {
			// Child element is what we're looking for.
			if (subElement != null) {
				error(elementName + " must not contain more than one sub-element", ele);
			}
			else {
				subElement = (Element) node;
			}
		}
	}
	// 解析constructor-arg上的ref属性
	boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
	// 解析constructor-arg上的value属性
	boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
	
	if ((hasRefAttribute && hasValueAttribute) ||
			((hasRefAttribute || hasValueAttribute) && subElement != null)) {
		error(elementName +
				" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
	}
	// ref属性的处理,最终用RuntimeBeanReference去封装对应的ref名称
	if (hasRefAttribute) {
		String refName = ele.getAttribute(REF_ATTRIBUTE);
		if (!StringUtils.hasText(refName)) {
			error(elementName + " contains empty 'ref' attribute", ele);
		}
		RuntimeBeanReference ref = new RuntimeBeanReference(refName);
		ref.setSource(extractSource(ele));
		return ref;
	}
	// value属性的处理,使用TypedStringValue封装
	else if (hasValueAttribute) {
		TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
		valueHolder.setSource(extractSource(ele));
		return valueHolder;
	}
	// 解析子元素
	else if (subElement != null) {
		return parsePropertySubElement(subElement, bd);
	}
	else {
		// 若以上情况都不满足,说明配置有问题
		error(elementName + " must specify a ref or value", ele);
		return null;
	}
}

再来看下Spring对Constructor-arg的子元素是如何解析的:

parsePropertySubElement(subElement, bd);
↓↓↓↓↓↓
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
	return parsePropertySubElement(ele, bd, null);
}
↓↓↓↓↓↓
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
	if (!isDefaultNamespace(ele)) {
		return parseNestedCustomElement(ele, bd);
	}
	else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
		// ...
	}
	else if (nodeNameEquals(ele, REF_ELEMENT)) {
		// ...
	}
	else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
		return parseIdRefElement(ele);
	}
	// 解析 value 标签
	else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
		return parsevalueElement(ele, defaultValueType);
	}
	// 解析 null 标签
	else if (nodeNameEquals(ele, NULL_ELEMENT)) {
		TypedStringValue nullHolder = new TypedStringValue(null);
		nullHolder.setSource(extractSource(ele));
		return nullHolder;
	}
	// 解析 array 标签
	else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
		return parseArrayElement(ele, bd);
	}
	// 解析 list 标签
	else if (nodeNameEquals(ele, LIST_ELEMENT)) {
		return parseListElement(ele, bd);
	}
	// 解析 set 标签
	else if (nodeNameEquals(ele, SET_ELEMENT)) {
		return parseSetElement(ele, bd);
	}
	// 解析 map 标签
	else if (nodeNameEquals(ele, MAP_ELEMENT)) {
		return parseMapElement(ele, bd);
	}
	// 解析 props 标签
	else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
		return parsePropsElement(ele);
	}
	else {
		error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
		return null;
	}
}

到这里,我们可以了解Spring会对可支持的子类进行分类处理,而具体的处理就不再一一展开细说了。

(6) 解析property子元素

老样子,先来看下它有什么作用:

@org.junit.jupiter.api.Test
public void test() {
	BeanFactory factory = new DefaultListableBeanFactory();
	XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) factory);

	ClassPathResource resource = new ClassPathResource("user.xml");
	reader.loadBeanDefinitions(resource);

	User user = (User) factory.getBean("user");
	System.out.println(user.getId());
}

user.xml:


	

结果如下:

看来,其也就是用于给类的字段赋值的,我们来看下其源码。

parsePropertyElements(ele, bd);
↓↓↓↓↓↓
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
	NodeList nl = beanEle.getChildNodes();
	for (int i = 0; i < nl.getLength(); i++) {
		Node node = nl.item(i);
		if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
			parsePropertyElement((Element) node, bd);
		}
	}
}
↓↓↓↓↓↓
public void parsePropertyElement(Element ele, BeanDefinition bd) {
	String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
	if (!StringUtils.hasLength(propertyName)) {
		error("Tag 'property' must have a 'name' attribute", ele);
		return;
	}
	this.parseState.push(new PropertyEntry(propertyName));
	try {
		if (bd.getPropertyValues().contains(propertyName)) {
			error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
			return;
		}
		// 关键代码
		Object val = parsePropertyValue(ele, bd, propertyName);
		PropertyValue pv = new PropertyValue(propertyName, val);
		parsemetaElements(ele, pv);
		pv.setSource(extractSource(ele));
		bd.getPropertyValues().addPropertyValue(pv);
	}
	finally {
		this.parseState.pop();
	}
}

Object val = parsePropertyValue(ele, bd, propertyName);这行代码,我们在第5小节,解析constructor-arg标签的时候就已经讲过了,无非就是解析ref、value以及子元素(set、map等标签)的过程,这里也就不做展开。

最终解析好的属性则会记录在BeanDefinition中的propertyValues属性中。

(7) qualifier子元素

Spring框架中进行自动装配时,Spring容器中匹配的候选Bean的数量有且只有一个。 Spring允许我们通过Qualifier指定注入Bean的名称,这样可以用于消除歧义。 若找不到一个匹配的bean时,Spring容器将抛出BeanCreationnException异常。

贴出注解形式的伪代码:

public interface User {
    void log();
}

@Service("teacher")
public class Teacher implements User {
    @Override
    public void log() {
        System.out.println("i am teacher");
    }
}

@Service("student")
public class Student implements User {
    @Override
    public void log() {
        System.out.println("i am student");
    }
}

public class Test {
    @Autowired
    @Qualifier("student")
    private User user;

    @RequestMapping("/test")
    public void test() {
        // 输出:i am student
        user.log();
    }
}
1.1.2 AbstractBeanDefinition的属性

1.1.1节中,对标签的解析和属性的承载,也就是完成了XML文档到GenericBeanDefinition的转换。其中XML的所有配置都可以在GenericBeanDefinition的实例类中找到对应的位置。大部分的通用属性则保存在AbstractBeanDefinition中。

我们来看下这个类中的属性:

public abstract class AbstractBeanDefinition extends BeanmetadataAttributeAccessor
		implements BeanDefinition, Cloneable {
	private volatile Object beanClass;
	// bean 的作用范围,对应bean的scope属性
	private String scope = SCOPE_DEFAULT;
	// 是否抽象,对应bean的abstract
	private boolean abstractFlag = false;
	// 是否延迟加载。对应bean的lazy-init
	private Boolean lazyInit;
	// 自动注入模式,对应bean的autowire
	private int autowireMode = AUTOWIRE_NO;
	private int dependencyCheck = DEPENDENCY_CHECK_NONE;
	// 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean标签下的depend-on
	private String[] dependsOn;
	// 设置为false的话,表示容器在查找自动装配的对象的时候,将不考虑该bean,对应autowire-candidate
	private boolean autowireCandidate = true;
	// 当自动装配出现多个bean的候选者的时候,作为首选者
	private boolean primary = false;
	// 用于记录Qualifier,对影子元素qualifier
	private final Map qualifiers = new linkedHashMap<>();

	private Supplier instanceSupplier;
// 允许访问非公开的构造器和方法,由程序设计
	private boolean nonPublicAccessAllowed = true;
	
	private boolean lenientConstructorResolution = true;
	// 对应bean属性的factory-bean
	private String factoryBeanName;

	private String factoryMethodName;
	// 记录构造函数注入的属性,对于bean属性的constructor-arg
	private ConstructorArgumentValues constructorArgumentValues;
	// 普通属性集合
	private MutablePropertyValues propertyValues;
	// 方法重写的持有者,记录lookup-method和replaced-method元素
	private MethodOverrides methodOverrides = new MethodOverrides();
	// 初始化方法,对应init-method
	private String initMethodName;
	// 销毁方法,对应destroy-method
	private String destroyMethodName;
	// 是否执行初始化方法,程序设置
	private boolean enforceInitMethod = true;
	// 是否执行销毁方法,程序设置
	private boolean enforceDestroyMethod = true;
	// 是否是用户定义的,创建AOP的时候为true,程序设置
	private boolean synthetic = false;
	
	private int role = BeanDefinition.ROLE_APPLICATION;
	// bean的描述信息
	private String description;
	// bean定义的资源
	private Resource resource;
}
1.1.3 解析默认标签中的自定义标签

再从头看起:默认标签的解析processBeanDefinition方法:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	// 1.进行Bean元素解析,返回的实例bdHolder中,已经包含配置文件里面的各种属性了,例如:class、name、id、alias等。
	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) {
			// ...
		}
		// 4.发送响应事件,通知相关监听器,该bean已经加载完成了。
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

上文已经把第一步中,对配置文件的一个基础解析和提取已经讲完了。至此我们继续第二步,解析默认标签下的自定义属性bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
什么叫默认标签下的自定义标签呢?上代码:


	

注意,这里的自定义标签对应的是bean 的一个属性。

bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
↓↓↓↓↓↓
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
	// 都三个参数的作用:父类bean,当对某个嵌套配置进行分析时,需要用到父类的scope属性
	// 而当前分析默认标签的自定义标签这个场景下,分析的是顶层配置,因此传递null
	return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
↓↓↓↓↓↓
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
			Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

	BeanDefinitionHolder finalDefinition = originalDef;

	// 遍历所有的属性,看看是否有适用于修饰的属性
	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
}

从代码中我们可以看出来,无论是哪一个代码块,最终都执行了decorateIfRequired方法:

public BeanDefinitionHolder decorateIfRequired(
			Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
	// 1.获取自定义标签的命名空间
	String namespaceUri = getNamespaceURI(node);
	if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
		// 2.根据命名空间找到对应的处理器
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler != null) {
			// 3.进行修饰
			BeanDefinitionHolder decorated =
					handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
			if (decorated != null) {
				return decorated;
			}
		}
		// ...
	}
	return originalDef;
}
    获取属性或元素的命名空间。判断该元素是否适用于自定义标签解析的条件。找到自定义类型对应的handler进行进一步解析(第二章节具体展开)。
1.1.3 注册解析的BeanDefinition

对于配置文件,Spring容器负责加载解析,将其转化为document实例。而上述流程则是将配置解析完了。相关的装饰功能也做好了,那么此时BeanDefinition已经满足使用的要求了,剩下的工作即是注册了。

代码入口如下:

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

我们来深入了解:

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

		// 1.使用beanName作为唯一的标识key注册
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// 2.注册所有的别名
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}
}

可见对于BeanDefinition的注册分成了两个部分:

    通过beanName的注册。通过别名的注册。
(1) 通过beanName注册

整体而言,我们都将直到Spring容器中存储BeanDefinition的方式就是放在map中,beanName则作为map的key。具体的我们来看下:

registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
↓↓↓↓↓↓↓
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
		// 1.beanName和对应的BeanDefinition肯定不能为空或者null,否则我还注册啥呢?注册也就是将其变成key-value而已
		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				// 2.进行注册前的最后一次校验
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		// 若该beanName已经被注册
		if (existingDefinition != null) {
			// 3.若对于的beanName已经注册并且在配置中配置了bean不可以被覆盖,那么此时抛出异常
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			// ...各种log记录
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			if (hasBeanCreationStarted()) {
				// beanDefinitionMap作为全局变量,可能存在并发访问的情况
				synchronized (this.beanDefinitionMap) {
					// 4.加入map缓存,该BeanDefinition通过注册
					this.beanDefinitionMap.put(beanName, beanDefinition);
					// 5.更新beanName列表
					List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					removeManualSingletonName(beanName);
				}
			}
			else {
				// 此时还在启动注册的阶段,记录以下beanName
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				// 重置所有beanName对应的缓存
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}
		// 单例bean只能注册一次
		if (existingDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
		else if (isConfigurationFrozen()) {
			clearByTypeCache();
		}
	}
}

注册前的最后一次校验:((AbstractBeanDefinition) beanDefinition).validate();

public void validate() throws BeanDefinitionValidationException {
	// 主要是对AbstractBeanDefinition中的methodOverrides进行校验
	// 校验methodOverrides是否和工厂方法并存,或者methodOverrides对应的方法不存在。
	if (hasMethodOverrides() && getFactoryMethodName() != null) {
		throw new BeanDefinitionValidationException(
				"Cannot combine factory method with container-generated method overrides: " +
				"the factory method must create the concrete bean instance.");
	}
	if (hasBeanClass()) {
		prepareMethodOverrides();
	}
}

总结下就是:

    校验methodOverrides属性。处理beanName被注册但是不允许被覆盖的情况。加入map进行注册。清除缓存。
(2) 通过别名注册
registry.registerAlias(beanName, alias);
↓↓↓↓↓↓↓
public class SimpleAliasRegistry implements AliasRegistry {
	@Override
	public void registerAlias(String name, String alias) {
		Assert.hasText(name, "'name' must not be empty");
		Assert.hasText(alias, "'alias' must not be empty");
		synchronized (this.aliasMap) {
			// 若beanName和别名alias相同,则不记录alias,并删除它,
			if (alias.equals(name)) {
				this.aliasMap.remove(alias);
				if (logger.isDebugEnabled()) {
					logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
				}
			}
			else {
				String registeredName = this.aliasMap.get(alias);
				if (registeredName != null) {
					// 若已经存在了,则不需要重复注册
					if (registeredName.equals(name)) {
						return;
					}
					// 同样考虑到alias是否允许被覆盖
					if (!allowAliasOverriding()) {
						throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
								name + "': It is already registered for name '" + registeredName + "'.");
					}
					if (logger.isDebugEnabled()) {
						logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
								registeredName + "' with new target name '" + name + "'");
					}
				}
				// 检查是否存在循环引用,例如”A->B存在时,若出现A->C->B则抛出异常
				checkForAliasCircle(name, alias);
				this.aliasMap.put(alias, name);
				if (logger.isTraceEnabled()) {
					logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
				}
			}
		}
	}
}

到这里,bean标签的解析和注册已经结束,但这个仅仅是针对默认的标签解析,而其他的allias、import、beans标签,本文不再展开描述。

二. 自定义标签的解析

讲完parseDefaultElement方法后,也就是默认标签的解析流程。我们接下来去了解下自定义标签的使用和解析原理parseCustomElement方法。

2.1 自定义标签的运用

扩展一个自定义标签的配置大概需要这么几个步骤:

    创建一个需要扩展的组件。定义一个XSD文件描述组件的内容。创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义。创建一个Handler文件,扩展自NamespaceHandlerSupport,用于将组件注册到Spring容器中。编写Spring.handlers和Spring.schemas文件。

案例,项目结构如下:

1.创建一个需要扩展的组件User。

package com.mytest;

public class User {
	private String email;
	private String name;

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

2.定义一个XSD文件描述组件的内容,user.xsd,这里定义了3种标签属性,id、name、email



	
		
			
			
			
		
	

3.创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义,UserBeanDefinitionParser:

package com.mytest;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

public class UserBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {
	protected Class getBeanClass(Element element) {
		return User.class;
	}

	protected void doParse(Element element, BeanDefinitionBuilder bean) {
		String name = element.getAttribute("name");
		String email = element.getAttribute("email");
		if (StringUtils.hasText(name)) {
			bean.addPropertyValue("name", name);
		}
		if (StringUtils.hasText(email)) {
			bean.addPropertyValue("email", email);
		}
	}
}

4.创建一个Handler文件,扩展自NamespaceHandlerSupport,用于将组件注册到Spring容器中,UserNamespaceHandler:

package com.mytest;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class UserNamespaceHandler extends NamespaceHandlerSupport {
	@Override
	public void init() {
		registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
	}
}

5.编写Spring.handlers和Spring.schemas文件。
Spring.handlers:

http://www.ljjTest.com/schema/user=com.mytest.UserNamespaceHandler

Spring.schemas:

http://www.ljjTest.com/schema/user.xsd=meta-INF/user.xsd

6.编写XML配置Bean。user.xml:




	

7.测试类Test:

package com.mytest;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;

public class Test {
	@org.junit.jupiter.api.Test
	public void test() {
		BeanFactory factory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) factory);

		ClassPathResource resource = new ClassPathResource("user.xml");
		reader.loadBeanDefinitions(resource);

		User user = (User) factory.getBean("user");
		System.out.println(user.getName());
		System.out.println(user.getEmail());
	}
}

结果如下:

Spring加载自定义标签的流程大概是这样:

    遇到自定义标签后,就去Spring.handlers和Spring.schemas文件中去找到对应的解析handler和XSD。默认位置是/meta-INF/下。找对于的handler后,自然而然能够找到解析元素的Parser。进而完成整个自定义元素的解析。

2.2 自定义标签解析原理

在了解自定义标签的使用和大致流程后,接下来让我们来看下Spring是如何对自定义标签进行解析的。让我们继续从parseCustomElement这个方法作为入口来解析:

public class BeanDefinitionParserDelegate {
	public BeanDefinition parseCustomElement(Element ele) {
		return parseCustomElement(ele, null);
	}
}
↓↓↓↓↓↓
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	// 1.获取对应的命名空间(用的是第三方org.w3c.dom.Node的方法,不做深入了解)
	String namespaceUri = getNamespaceURI(ele);
	if (namespaceUri == null) {
		return null;
	}
	// 2.根据命名空间找到对应的Handler
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	if (handler == null) {
		error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
		return null;
	}
	// 3.调用自定义的Handler进行解析
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

我们来看下第二步,提取自定义标签处理器。

2.2.1 提取自定义标签处理器
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
	public NamespaceHandler resolve(String namespaceUri) {
		// 1.获取所有已经配置的handler映射
		Map handlerMappings = getHandlerMappings();
		// 2.根据命名空间找到对应的handler
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			// 3.若已经做过了解析,那么直接从缓存读取
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			// 4.若没做过解析,拿到的是类路径
			String className = (String) handlerOrClassName;
			try {
				// 5.通过反射,根据类路径拿到handler
				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");
				}
				// 6.初始化
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
				// 7.调用自定义的namespaceHandler初始化方法
				
				namespaceHandler.init();
				// 8.记录在缓存中
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			// ...catch
		}
	}
}

上述代码很好理解,无非就是根据我们Spring.handlers配置文件中的映射地址,去找到对应的Handler,通过反射去调用其初始化方法,来提取自定义标签处理器。 那么Spring是如何读取配置文件的呢?我们来看下第一步代码

Map handlerMappings = getHandlerMappings();
↓↓↓↓↓↓
private Map getHandlerMappings() {
	// 1.如果没有被缓存则开始缓存
	Map handlerMappings = this.handlerMappings;
	if (handlerMappings == null) {
		synchronized (this) {
			handlerMappings = this.handlerMappings;
			if (handlerMappings == null) {
				if (logger.isTraceEnabled()) {
					logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
				}
				try {
					// this.handlerMappingsLocation在构造函数调用的时候就会初始化为meta-INF/Spring.handlers
					Properties mappings =
							PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
					if (logger.isTraceEnabled()) {
						logger.trace("Loaded NamespaceHandler mappings: " + mappings);
					}
					handlerMappings = new ConcurrentHashMap<>(mappings.size());
					// 将Properties格式文件合并到map中
					CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
					this.handlerMappings = handlerMappings;
				}
				catch (IOException ex) {
					throw new IllegalStateException(
							"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
				}
			}
		}
	}
	return handlerMappings;
}

我们来看下其构造:

public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "meta-INF/spring.handlers";
↓↓↓↓↓↓
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
	this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
↓↓↓↓↓↓
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader, String handlerMappingsLocation) {
	Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
	this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
	this.handlerMappingsLocation = handlerMappingsLocation;
}

可见我们在固定的文件目录下创建固定名称的文件是有意义的(meta-INF/spring.handlers)

2.2.2 标签解析

上述代码中,我们只讲完了Handler的初始化工作,还没有讲其解析的工作,我们来看下第三步:

return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

代码定位到NamespaceHandlerSupport类中:

public abstract class NamespaceHandlerSupport implements NamespaceHandler {
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		// 寻找解析器并进行解析操作
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		return (parser != null ? parser.parse(element, parserContext) : null);
	}
}

解析的第一步肯定是找到对应的解析器,而我们Handler在初始化的时候,注册了自己的解析器UserBeanDefinitionParser:

public void init() {
	registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}

我们来看下findParserForElement方法:

private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
		// 1.获取元素名称,也就是 

最后对于解析的处理,定位到AbstractBeanDefinitionParser类:

public abstract class AbstractBeanDefinitionParser implements BeanDefinitionParser {
	public final BeanDefinition parse(Element element, ParserContext parserContext) {
		// 1.做一系列的数据准备,对beanClass、scope、lazyInit等属性的准备
		AbstractBeanDefinition definition = parseInternal(element, parserContext);
		if (definition != null && !parserContext.isNested()) {
			try {
				// 解析id
				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);
				}
				// 解析别名
				String[] aliases = null;
				if (shouldParseNameAsAliases()) {
					String name = element.getAttribute(NAME_ATTRIBUTE);
					if (StringUtils.hasLength(name)) {
						aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
					}
				}
				// 将解析出来的结果存于holder实例,并注册BeanDefinition
				BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
				registerBeanDefinition(holder, parserContext.getRegistry());
				if (shouldFireEvents()) {
					// 通知监听器进行处理
					BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
					postProcessComponentDefinition(componentDefinition);
					parserContext.registerComponent(componentDefinition);
				}
			}
			catch (BeanDefinitionStoreException ex) {
				String msg = ex.getMessage();
				parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
				return null;
			}
		}
		return definition;
	}
}

这里就和上文的默认标签解析的流程大致相同了,解析Bean属性、用holder存放数据、再注册等等。文章到这里也就讲完了。讲到这里,Spring对标签(默认、自定义)的解析、处理都已经结束。而Spring中的全部解析工作也已经完成,即:bean从配置文件到加载到内存的全部过程。

三. 大总结☆

Spring对标签的解析分为两种:

    默认标签解析(Spring原生自带的)parseDefaultElement方法。自定义标签解析parseCustomElement方法。

本文对标签的解析是在Spring读取解析好XML配置并将其转化为document对象之后。通过解析document对象来解析标签获得可用的BeanDefinition。

parseDefaultElement流程(一共4种:、、),以最常见的标签的解析为例,

下一篇文章准备学习下bean的加载。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/756728.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号