前言

本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本。因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析。

本篇文章主要介绍 Spring IoC 容器怎么解析默认标签的。

正文

所谓的默认标签就是 importaliasbeanbeans 这四个标签。

DefaultBeanDefinitionDocumentReader#parseDefaultElement

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标签的处理
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
doRegisterBeanDefinitions(ele);
}
}

上面 parseDefaultElement 方法中对 bean 标签的处理方法 processBeanDefinition 最为重要,下面来着重分析一下。

DefaultBeanDefinitionDocumentReader#processBeanDefinition

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 将ele解析成BeanDefinitionHolder,见下文详解
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 若存在默认标签下的子节点下不再有自定义属性,需要再次对自定义标签再进行解析(基本不用,不做深入分析)
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 注册最终的BeanDefinition,见下文详解
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 发出响应事件,通知相关监听器,这个bean已经注册完
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

BeanDefinitionParseDelegate#parseBeanDefinitionElement

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
// 解析元素,封装成BeanDefinitionHolder
return parseBeanDefinitionElement(ele, null);
} public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
// 将name属性所有的名称按照逗号或者分号(,;),分割成数组放入别名集合aliases
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// beanName默认使用id
String beanName = id;
// 没有指定id属性 && 指定了name属性
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 如果没有指定id,beanName等于第一个别名,剩下的依然作为别名使用
beanName = aliases.remove(0);
} if (containingBean == null) {
// 验证beanName和aliases是否在同一个<beans>下已经存在
checkNameUniqueness(beanName, aliases, ele);
}
// 将元素解析成GenericBeanDefinition
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// 如果不存在beanName会根据Spring的命名规则生成一个
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);
}
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 用beanDefinition和beanName以及aliasesArray构建BeanDefinitionHolder
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
} public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null;
// 获取class属性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
// 获取parent属性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
} try {
// 创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 解析默认bean的各种属性,见下文详解
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析元数据,见下文详解
parseMetaElements(ele, bd);
// 解析lookup-method属性,很少使用,不具体介绍
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析replaced-method属性,很少使用,不具体介绍
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析constructor-arg属性,见下文详解
parseConstructorArgElements(ele, bd);
// 解析property属性,见下文详解
parsePropertyElements(ele, bd);
// 解析qualifier属性,见下文详解
parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
// 省略异常处理...
finally {
this.parseState.pop();
}
return null;
}

上面方法中的 BeanDefinitionReaderUtils.generateBeanName() 方法会为 bean 生成一个默认的名称,主要规则如下:

  1. 获取 bean 的全类名,例如 com.leisurexi.ioc.domain.User;如果 beanparent 不为空那么 bean 的名称为 parentName 加上 $child,例如 userparent 是名称为 parentUserbean,那么当 user 未指定 beanName 时会其生成一个 parentUser$child 的名称;如果 beanparent 为空但 factory-bean 属性不为空,那就用该名称加上 $created 为其生成名称。
  2. 未指定 parentfactory-bean 属性,那么如果是内嵌 bean 则用全类名加上 # 和转换为十六进制字符串的 hashcode 拼成的字符串当做名称;不是内嵌 bean 就用全类名加上 # 和数字当做名称,如第一个 User 类型的自动生成的名称为 com.leisurexi.ioc.domain.User#0,第二个就是 com.leisurexi.ioc.domain.User#1

我们这边可以简单看一下 BeanDefinitionHolder 的属性,如下:

public class BeanDefinitionHolder implements BeanMetadataElement {

    // bean 的定义元信息
private final BeanDefinition beanDefinition;
// bean 的名称
private final String beanName;
// bean 的别名数组
@Nullable
private final String[] aliases; // 省略其它代码...
}

BeanDefinitionParserDelegate#parseBeanDefinitionAttributes

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// 解析singleton属性
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// singleton属性已经不支持了,使用了会直接抛出异常,请使用scope属性替代
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
// 解析scope属性
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
// 如果是内嵌bean,则使用上级bean的scope值
else if (containingBean != null) {
bd.setScope(containingBean.getScope());
}
// 解析abstract属性
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
// 解析lazy属性
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
// 若没有设置或者设置成其他字符都会被设置为默认值false
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
// 解析autowire属性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
// 解析depends-on属性
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
} // 解析autowire-candidate属性,该属性为false代表该bean不会被选为依赖注入的对象,默认为true
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));
}
// 解析primary属性
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
// 解析init-method属性
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
// 如果bean没有指定init-method属性,但beans标签指定了default-init-method属性,则会使用该属性
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
// 解析destroy-method属性
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
// 如果bean没有指定destroy-method属性,但beans标签指定了default-destroy-method属性,则会使用该属性
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
// 解析factory-method属性
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
// 解析factory-bean属性
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
} return bd;
}

上面方法完成了对所有 bean 标签属性的解析。值得注意的地方是如果同时指定了 bean 标签的 init-methodbeans 标签的 default-init-method 属性,那么优先使用前者,destory-mehtod 标签也是一样。

大家可以去看一下 AbstractBeanDefinition 中定义的属性就一目了然了,这里限于篇幅原因就不展示了。

BeanDefinitionParserDelegate#parseConstructorArgElements

public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
// 获取所有子节点
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 提取 constructor-arg
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
// 解析 constructor-arg
parseConstructorArgElement((Element) node, bd);
}
}
}
// <constructor-arg index="0" type="" value=""/>
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);
// index 不为空
if (StringUtils.hasLength(indexAttr)) {
try {
// 转换为 int 类型
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
// 解析属性值,见下文详解
Object value = parsePropertyValue(ele, bd, null);
// 使用 ConstructorArgumentValues.ValueHolder 类型来封装解析出来的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
// 如果 type 不为空,设置 ValueHolder 的 type
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
// 如果 name 不为空,设置 ValueHolder 的 name
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
// 判断 index 是否重复,重复则抛出异常
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
// 将 index 和 valueHolder 以 key-value的形式 添加到 BeanDefinition 的 ConstructorArgumentValues 当中
else {
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 {
try {
// 这里就是 constructor-arg 标签中没有指定 index 属性
this.parseState.push(new ConstructorArgumentEntry());
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));
// 将 valueHolder 添加 BeanDefinition 的 GenericArgumentValue 中
// 这里和上面的 IndexedArgumentValue 类似,只不过上面是 Map,这里是 List
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}

上面代码首先提取 constructor-arg 标签中必要的属性 (indextypename)。

  • 如果指定了 index 属性:

    1. 解析 constructor-arg 的子元素。
    2. 使用 ConstructorArgumentsValues.ValueHolder 类型来封装解析出来的元素。
    3. typenameindex 属性一并封装在 ConstructorArgumentsValues.ValueHolder 类型中,并添加到当前 BeanDefinitionConstructorArgumentValues 中的 LinkedHashMap 类型的属性indexedArgumentValues 中。
  • 如果没有指定 index 属性:
    1. 解析 constructor-arg 的子元素。
    2. 使用 ConstructorArgumentsValues.ValueHolder 类型来封装解析出来的元素。
    3. typenameindex 属性一并封装在 ConstructorArgumentsValues.ValueHolder 类型中,并添加到当前 BeanDefinitionConstructorArgumentValues 中的 ArrayList 类型的属性genericArgumentValues 中。

BeanDefinitionParserDelegate#parsePropertyValue

public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// 获取所有子节点,例如list、map等
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;
}
}
}
// 提取 ref 属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 提取 value 属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
// 如果同时有 ref 和 value 属性 || 有 ref 或 value 属性同时又有子元素,抛出异常
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 名称 (该 ref 值指向另一个 bean 的 beanName)
// RuntimeBeanReference 起到占位符的作用, ref 指向的 beanName 将在运行时被解析成真正的 bean 实例引用
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 {
// 没有子元素,也没有 ref 和 value,直接抛出异常
error(elementName + " must specify a ref or value", ele);
return null;
}
} public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
} public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
// 校验是否为默认的命名空间,如果不是则走解析自定义节点代码
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
// 解析 bean 节点
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
// 解析 ref 节点
else if (nodeNameEquals(ele, REF_ELEMENT)) {
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean' or 'parent' is required for <ref> element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
// 解析 idref 节点
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;
}
}

从上面的代码来看,对构造函数中属性元素的解析,步骤如下:

  1. 略过 description 或者 meta
  2. 提取 constructor-arg 上的 refvalue 属性,以便于根据规则验证正确性。其规则为在 constructor-arg 上不存在以下情况:
    • 同时存在 refvalue 属性。
    • 存在 ref 或者 value 属性,并且又有子元素。

BeanDefinitionParserDelegate#parsePropertyElements

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)) {
// 解析 property 节点
parsePropertyElement((Element) node, bd);
}
}
} // 这里是解析 property 标签,<property name="..." value="..."/>
public void parsePropertyElement(Element ele, BeanDefinition bd) {
// 获取 name 属性
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
// name 为空,抛出异常
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
// 出现两个 name 相同的抛出异常
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
// 解析属性值,跟构造器解析一样
Object val = parsePropertyValue(ele, bd, propertyName);
// 用 name 和 val 封装成 PropertyValue
PropertyValue pv = new PropertyValue(propertyName, val);
// 解析元数据,跟 beans 标签内的 meta 一样
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
// 添加到 BeanDefinition 的 PropertyValues 属性中
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}

上面方法主要是遍历 property 节点,然后解析属性值封装成 PropertyValue 添加到 BeanDefinitionPropertyValues 中。

注意:property 标明的属性必需有 set 方法否则在赋值阶段会抛出异常。

BeanDefinitionParserDelegate#parseQualifierElements

public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
// 获取子节点
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
// 解析 qualifier 节点
parseQualifierElement((Element) node, bd);
}
}
} public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
// 提取 type
String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
// type 为空抛出异常
if (!StringUtils.hasLength(typeName)) {
error("Tag 'qualifier' must have a 'type' attribute", ele);
return;
}
this.parseState.push(new QualifierEntry(typeName));
try {
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
qualifier.setSource(extractSource(ele));
// 提取 value
String value = ele.getAttribute(VALUE_ATTRIBUTE);
// value 不为空,设置到 AutowireCandidateQualifier 的 attribute 中
if (StringUtils.hasLength(value)) {
qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
}
// 获取子节点
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 如果是有 attribute 节点,进行解析,提取值放入到 AutowireCandidateQualifier 的MetadataAttribute 中
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
Element attributeEle = (Element) node;
String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
attribute.setSource(extractSource(attributeEle));
qualifier.addMetadataAttribute(attribute);
}
else {
error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
return;
}
}
}
// 设置 BeanDefinition 的 qualifier
bd.addQualifier(qualifier);
}
finally {
this.parseState.pop();
}
}

对于 qualifier 元素的获取,我们大多数接触的更多是注解的形式,在使用 Spring 框架中进行自动注入时,Spring 容器中匹配的候选 Bean 必需有且只有一个。如果存在多个类型相同的 Bean,且按照类型注入时,Spring 允许通过 qualifier 指定注入 Bean 的名称,这样歧义就消除了。

BeanDefinitionReaderUtils#registerBeanDefinition

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

    // Register bean definition under primary name.
// 获取 beanName
String beanName = definitionHolder.getBeanName();
// 以 key-value 的形式注册,key 为 beanName,value 为 BeanDefinition。见下文详解
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any.
// 注册 bean 的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// 以 key-value 的形式注册 bean 的别名,key 为别名,value 为 beanName。见下文详解
registry.registerAlias(beanName, alias);
}
}
} // DefaultListableBeanFactory.java
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 {
// 验证 Bean 的格式是否正确
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
} BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// 这里判断 BeanDefinition 是否存在
if (existingDefinition != null) {
// 这里就是如果 Bean 定义以及存在,判断是否可以覆盖,默认是可以的
// Spring Boot 2.1开始这里会手动设置 allowBeanDefinitionOverriding 的值为 false
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
// 将 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 缓存中
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// bean 是否已经开始创建
if (hasBeanCreationStarted()) {
synchronized (this.beanDefinitionMap) {
// 将 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 缓存中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 这里将 beanDefinitionNames 写时复制一份,类似于 CopyOnWriteArrayList
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
// 从单例 Bean 注册名称列表中删除当前 beanName
removeManualSingletonName(beanName);
}
}
// bean 不在创建状态中
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
// 因为 ConcurrentHashMap 是无序的,这里将 beanName 放入 ArrayList,记录注册顺序
this.beanDefinitionNames.add(beanName);
// 从单例 Bean 注册名称列表中删除当前 beanName
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 如果存在相同的 beanName 的 BeanDefinition,或者 beanName 已经存在单例对象,则将该 beanName 对应的缓存信息、单例对象清除,因为这些对象都是由老的 BeanDefinition 创建的,需要被覆盖掉。再用新的 BeanDefinition 来创建这些缓存和单例对象
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
} // SimpleAliasRegistry.java
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 相同,从缓存中移除
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
String registeredName = this.aliasMap.get(alias);
// 如果别名以及注册过,直接返回
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
// 如果不允许覆盖,抛出异常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'.");
}
}
// 检查 name 和 alias 是否存在循环引用。例如 A 的别名为 B,B的别名为A
checkForAliasCircle(name, alias);
// 将 alias 和 name 以 key-value 对放入到 aliasMap 中,进行缓存
this.aliasMap.put(alias, name);
}
}
}
}

上面代码有两个变量比较重要 beanDefinitionMapbeanDefinitionNames,下面代码是这两个属性在 DefaultListableBeanFactory 中的定义:

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { // 缓存 BeanDefinition 的 Map,key 为 beanName,value 为 BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 保存 BeanDefinition 的注册顺序,保存的是 beanName
private volatile List<String> beanDefinitionNames = new ArrayList<>(256); }

上面方法的主要流程如下:

  1. 如果 BeanDefinitionAbstractBeanDefinition 类型,验证 Bean 的格式是否正确。

    这次效验主要是对于 AbstractBeanDefinition 属性中的 methodOverrides 的校验,校验 methodOverrides 是否与 工厂方法 并存或者 methodOverrides 中的方法根本不存在。

  2. 判断该 beanNameBeanDefinition 是否已经注册过;如果存在判断是否允许覆盖,允许的话直接替换,不允许直接抛出异常。

    默认的情况下是允许的,但是在 Spring Boot 2.1 开始这里会手动的设置为不允许。

  3. beanName 对应的 BeanDefinition 以前没有注册过,判断 bean 是否已经开始创建;如果在创建中对 beanDefinitionMap 进行加锁 (这里虽然 beanDefinitionMap 是线程安全的 ConcurrentHashMap ,单个操作是线程安全的但多个操作不是,所以这里手动加锁),然后将 beanNameBeanDefinitionkey-value 形式放入 beanDefinitionMap 缓存中,然后写时复制一份 beanDefinitionNames ,将 beaName 缓存进去,记录 bean 的注册顺序;如果不在创建中直接将 BeanDefinitionbeanName 分别放入 beanDefinitionMapbeanDefinitionNames 中。

  4. 最后判断如果 BeanDefinition 已经注册过,或者 beanName 已经存在单例对象,则将该 beanName 对应的缓存信息、单例对象清除,因为这些对象都是由老的 BeanDefinition 创建的,需要被覆盖掉。再用新的 BeanDefinition 来创建这些缓存和单例对象。

总结

本文主要介绍了 Spring 对 XML 文件中 <bean> 标签的解析,我们可以重新梳理一下思路:

  1. 解析 <bean> 标签,构建成 AbstractBeanDefinition (GenericBeanDefinition) 对象来存放所有解析出来的属性。

  2. AbstractBeanDefinitionbeanNamealiasesArray 构建成 BeanDefinitionHolder 对象。

  3. 最后通过 BeanDefinitionHolderbeanNameBeanDefinition 注册到 DefaultListableBeanFactory 中,也就是保存起来。

    上文提到的两个比较重要的属性 beanDefinitionNamesbeanDefinitionMap ,在后面都会多次用到,可以重点关注一下。

最后,我模仿 Spring 写了一个精简版,代码会持续更新。地址:https://github.com/leisurexi/tiny-spring

参考

Spring IoC 默认标签解析的更多相关文章

  1. Spring IoC 自定义标签解析

    前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...

  2. spring源码深度解析— IOC 之 默认标签解析(上)

    概述 接前两篇文章  spring源码深度解析—Spring的整体架构和环境搭建  和  spring源码深度解析— IOC 之 容器的基本实现 本文主要研究Spring标签的解析,Spring的标签 ...

  3. spring源码深度解析— IOC 之 默认标签解析(下)

    在spring源码深度解析— IOC 之 默认标签解析(上)中我们已经完成了从xml配置文件到BeanDefinition的转换,转换后的实例是GenericBeanDefinition的实例.本文主 ...

  4. Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签

    写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...

  5. Spring IOC设计原理解析:本文乃学习整理参考而来

    Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...

  6. Spring IoC源码解析之invokeBeanFactoryPostProcessors

    一.Bean工厂的后置处理器 Bean工厂的后置处理器:BeanFactoryPostProcessor(触发时机:bean定义注册之后bean实例化之前)和BeanDefinitionRegistr ...

  7. Spring IoC源码解析之getBean

    一.实例化所有的非懒加载的单实例Bean 从org.springframework.context.support.AbstractApplicationContext#refresh方法开发,进入到 ...

  8. Spring系列(三):Spring IoC源码解析

    一.Spring容器类继承图 二.容器前期准备 IoC源码解析入口: /** * @desc: ioc原理解析 启动 * @author: toby * @date: 2019/7/22 22:20 ...

  9. 【Spring源码深度解析学习系列】默认标签解析(三)

    Spring的标签包括默认标签和自定义标签两种 默认标签的解析方法: ###DefaultBeanDefinitionDocumentReader.java### private void parse ...

随机推荐

  1. css 3 背景图片为渐变色(渐变色背景图片) 学习笔记

    6年不研究CSS发现很多现功能都没有用过,例如渐变色,弹性盒子等,年前做过一个简单的管理系统,由于本人美工不好,设计不出好看的背景图片,偶然百度到背景图片可以使用渐变色(感觉发现了新大陆).以后的项目 ...

  2. 内部服务器错误Internal server error解决方法

    这里说的内部服务器错误是网站前台能正常访问,而后台程序在执行某项任务/功能时所出现的内部服务器错误解决方法: 此错误通常是超时导致的,像程序在执行采集.静态页面生成时所耗时间太长导致达到超时限制的: ...

  3. "锁定文件失败 打不开磁盘或它所依赖的某个快照磁盘。模块启动失败。未能启动虚拟机"--解决方法

    今天正在使用kali的时候,电脑突然死机了..强制重启,在进入虚拟机发现报错: "锁定文件失败 打不开磁盘或它所依赖的某个快照磁盘.模块启动失败.未能启动虚拟机." 1.问题起因 ...

  4. Typora 使用 Markdown 嵌入 LaTeX 数学公式符号语法

    博客园不支持渲染 LaTeX 数学公式,需要用到什么公式,请复制到您所用的支持 LaTeX 的编辑器中查看实现效果.Typora 可以渲染 LaTeX 数学公式. 目录 行内与独行 行内公式 独行公式 ...

  5. Java实现 LeetCode 670 最大交换(暴力)

    670. 最大交换 给定一个非负整数,你至多可以交换一次数字中的任意两位.返回你能得到的最大值. 示例 1 : 输入: 2736 输出: 7236 解释: 交换数字2和数字7. 示例 2 : 输入: ...

  6. Java实现 LeetCode 514 自由之路

    514. 自由之路 视频游戏"辐射4"中,任务"通向自由"要求玩家到达名为"Freedom Trail Ring"的金属表盘,并使用表盘拼写 ...

  7. Java实现 LeetCode 268 缺失数字

    268. 缺失数字 给定一个包含 0, 1, 2, -, n 中 n 个数的序列,找出 0 - n 中没有出现在序列中的那个数. 示例 1: 输入: [3,0,1] 输出: 2 示例 2: 输入: [ ...

  8. Java重置Mysql主键自增长值

    MySql 主键自增重置器(统一处理多个表) resetAutoincrement 是一款基于 Java 开发的程序,其功能为重置 mysql 数据库表的主键自增的值为最近的一个. 介绍 开发背景主要 ...

  9. KVM NAT(网络地址转换模式)

    NAT(网络地址转换模式) 使用NAT模式,就是让虚拟系统借助NAT(网络地址转换)功能,通过宿主机器所在的网络来访问公网.也就是说,使用NAT模式可以实现在虚拟系统里访问互联网.很显然,如果你只有一 ...

  10. Linux系统如何设置开机自动运行脚本?

    大家好,我是良许. 在工作中,我们经常有个需求,那就是在系统启动之后,自动启动某个脚本或服务.在 Windows 下,我们有很多方法可以设置开机启动,但在 Linux 系统下我们需要如何操作呢? Li ...