抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

SpringIOC-默认标签解析

默认标签解析

parseDefaultElement

接着上文,调用parseDefaultElement主要负责默认标签的解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 默认标签的解析
* 默认标签主要有import标签alias标签,bean标签,beans标签以及
* @param ele
* @param delegate
*/
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//import标签解析 重要程度 1 ,可看可不看
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
//alias标签解析 别名标签 重要程度 1 ,可看可不看
} else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
//bean标签,重要程度 5,必须看
} else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
// beans 标签解析 重新调回了 doRegisterBeanDefinitions
} else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}

​ 我们主要看processBeanDefinition,bean标签的解析,doRegisterBeanDefinitions主要负责beans标签解析,他的流程很简单,又重新调回了doRegisterBeanDefinitions重新开始解析

​ 可以看到默认命名空间的一级节点只有4种:import、alias、bean、beans。这4种节点中,最重要、最复杂的就是 <bean> 节点,本文只会介绍 <bean> 节点的处理,理解了 <bean> 节点后,其他的都不难理解。另外,<beans> 节点只是递归调用之前的 doRegisterBeanDefinitions 方法,因此无需再介绍。

接下来,让我们从 processBeanDefinition(ele, delegate) 方法正式开始

processBeanDefinition

解析bean标签 委托模式主要对<bean />这一类标签解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
* 解析bean标签 委托模式
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//重点看这个方法,重要程度 5 ,解析document,封装成BeanDefinition
// 1.进行节点定义解析, 经过这个方法后,bdHolder会包含一个Bean节点的所有属性,例如name、class、id
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//2.若存在默认标签的子节点下再有自定义属性,需要再次对自定义标签再进行解析
//该方法功能不重要,设计模式重点看一下,装饰者设计模式,加上SPI设计思想
/**
* 主要实现了命令标签的解析类似于
* p:username="Jack" p:password="123" c:age="12" c:sex="1"
*/
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 3.解析节点定义完成后,需要对解析后的bdHolder进行注册
//完成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.
// 4.最后发出响应事件,通知相关的监听器,这个Bean已经加载完成了
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

核心方法是parseBeanDefinitionElement,解析bean的各种元素

parseBeanDefinitionElement

bean标签的解析,将标签元素转成BeanDefinitionHolder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
//解析元素
return parseBeanDefinitionElement(ele, null);
}
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* <p>
* bean标签的解析,将标签元素转成BeanDefinitionHolder
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 1.解析name和id属性
// 获取ID元素
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取name元素
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//别名解析
// 分割name属性(通过逗号或分号)
// 例如:<bean name="demoService,demoServiceAlias" class=""/>,分割后aliases为[demoService, demoServiceAlias]
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// beanName默认使用id
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 如果id为空,并且aliases不为空,则取aliases的第一个元素作为beanName,其他的仍作为别名
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) {
// 检查beanName和aliases是否在同一个 <beans> 下已经存在
checkNameUniqueness(beanName, aliases, ele);
}
// 2.进一步解析bean的其他所有属性并统一封装至GenericBeanDefinition类型实例中
//解析的核心方法 将element元素解析成beanDefinition 重要程度5
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
// 3.如果bean定义存在,但是beanName为空,则用Spring默认的生成规则为当前bean生成beanName
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
} else {
// Spring提供的生成规则生成beanName,例如:com.joonwhee.open.demo.service.impl.DemoServiceImpl#0
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)) {
// 如果Spring默认的生成规则生成的beanName为:类名加后缀,则将类名注册为别名
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);
// 4.将bean定义、beanName、bean别名数组封装成BeanDefinitionHolder
//创建BeanDefinitionHolder
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}

return null;
}

这里的核心方法是parseBeanDefinitionElement解析bean标签的各种元素

解析 name 和 id 属性,其中 name 属性可以通过分割符设置多个。如果 id 存在,则 使用 id 作为 beanName,name 属性分割后全部作为别名;如果 id 不存在,则将 name 属性分割后的第1个作为 beanName,剩下的全部作为别名。

举个例子:

1
2
3
4
5
<!-- 配置1 -->
<bean id="appleService" name="appleOne;appleTwo" class="com.joonwhee.AppleServiceImpl"/>

<!-- 配置2 -->
<bean name="bananaOne;bananaTwo" class="com.joonwhee.BananaServiceImpl"/>

配置1:beanName=appleService,aliases=[appleOne, appleTwo] ;

配置2:beanName=bananaOne,aliases=[bananaTwo]

parseBeanDefinitionElement

将Element 元素解析成 BeanDefinition,然后并将解析完成的BeanDefinition返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* Parse the bean definition itself, without regard to name or aliases. May return
* {@code null} if problems occurred during the parsing of the bean definition.
* 将Element 元素解析成 BeanDefinition
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {

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

String className = null;
// 1.解析class、parent属性
// 解析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 {
// 2.创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition
//创建GenericBeanDefinition对象
AbstractBeanDefinition bd = createBeanDefinition(className, parent);

//3. 解析bean标签的属性,并把解析出来的属性设置到BeanDefinition对象中
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//4. 提取description的描述信息
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

//解析元数据子节点(基本不用, 不深入介绍)
parseMetaElements(ele, bd);

//解析bean中的lookup-method标签 重要程度:2,可看可不看
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

//解析bean中的replaced-method标签 重要程度:2,可看可不看
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

//解析bean中的constructor-arg标签 重要程度:2,可看可不看
parseConstructorArgElements(ele, bd);

//解析bean中的property标签 重要程度:2,可看可不看
parsePropertyElements(ele, bd);

//解析bean中的Qualifier标签 可以不看,用不到
parseQualifierElements(ele, bd);
//设置Resource属性
bd.setResource(this.readerContext.getResource());
//设置Source属性
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.解析了class、parent属性,因为第2步创建 AbstractBeanDefinition 需要用到这两个属性,否则,这两个属性可以放到第3步一起解析。

2.创建用于承载属性的 AbstractBeanDefinition 类型的 GenericBeanDefinition。比较简单,直接 new 一个 GenericBeanDefinition,如果 className 和 classLoader 不为空,则通过反射构建出 BeanClass,并设置为 GenericBeanDefinition 的属性。

createBeanDefinition

来到BeanDefinitionReaderUtils类中

创建GenericBeanDefinition对象并进行parentName,beanClass等属性的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 创建GenericBeanDefinition对象
* 并对一些基础属性的设置
*
*/
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
//创建了一个GenericBeanDefinition对象
//GenericBeanDefinition 主要用于bean标签的封装
GenericBeanDefinition bd = new GenericBeanDefinition();
//设置父类名称
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
//设置bean的class
bd.setBeanClass(ClassUtils.forName(className, classLoader));
} else {
bd.setBeanClassName(className);
}
}
return bd;
}

解析标签元素

这里面涉及很多标签的解析,我们下面会对几个比较重要的分析一下

parseBeanDefinitionAttributes

解析bean标签的属性,并把解析出来的属性设置到BeanDefinition对象中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
/**
* 解析bean标签的属性,并把解析出来的属性设置到BeanDefinition对象中
*
*/
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// 解析singleton属性
// 对 singleton="true/false" 进行校验
//singleton 现已废弃使用 scop进行操作
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));
} else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
//在内部bean定义的情况下,从包含bean中获取默认值。
bd.setScope(containingBean.getScope());
}

// 是否包含属性 abstract 父子beanBeanDefinition
/**
* 父类parent会封装成beanDefinition但是不会实例化
* <!--这两个beanDefinition会在实例化过程中合并成一个-->
* <bean id="parent" class="com.xiangxue.jack.bean.Parent" abstract="true">
* <property name="username" value="parent name"/>
* </bean>
* <bean id="son" class="com.xiangxue.jack.bean.Son" parent="parent">
* <!--<property name="username" value="son name"/>-->
* <property name="age" value="18"/>
* </bean>
*
* 如果自类没有username属性则会集成父类的username属性parent name
*
*/
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
// 解析lazy-init属性, 默认为false
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
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 属性
//spring允许用户通过depends-on属性指定bean前置依赖的bean,前置依赖的bean会在本bean实例化之前创建好
// <bean id="manager" class="CacheManager" depends-on="sysinit"/> 前置依赖
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}

/**
* <bean class="com.xiangxue.jack.service.AccountServiceImpl" id="accountService"
* autowire-candidate="false" scope="singleton" primary="true"/>
* 如果 autowire-candidate 为false 表示该类不参与依赖注入
*/
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 如果存在多个只加载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);
} 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);
} else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
// 解析factory-method属性
//factory-method 工厂方法,通过factory-method来创建bean的实例
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
// 解析factory-bean属性
//factory-bean 工厂bean 通过另一个bean来创建该bean的实例 自己来做实例化不交给spring实例化
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}

return bd;
}

内容比较简单,就是从节点 ele 拿到所有的属性值,塞给 AbstractBeanDefinition 的对应属性。这些属性的使用如下图。

parseLookupOverrideSubElements

解析bean中的lookup-method标签

这里要注意一下lookup-method标签会被封装到BeanDefinition的MethodOverrides属性中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Parse lookup-override sub-elements of the given bean element.
* 解析bean中的lookup-method标签
*/
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
String beanRef = ele.getAttribute(BEAN_ELEMENT);
//LookupOverride对象主要用来包装lookup属性
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
//加入到overrides的容器
overrides.addOverride(override);
}
}
}

其他的parseReplacedMethodSubElements解析replaced-method标签也会被封装到BeanDefinition的MethodOverrides属性中

parseConstructorArgElements

解析bean中的constructor-arg标签,并将解析的数据封装到BeanDefinition的ConstructorArgumentValues属性中来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Parse constructor-arg sub-elements of the given bean element.
* 解析bean中的constructor-arg标签
*/
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
// 拿到beanEle节点的所有子节点
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)) {
// 解析constructor-arg
parseConstructorArgElement((Element) node, bd);
}
}
}

拿到 beanEle 节点的所有子节点,遍历解析所有是 constructor-arg 节点的子节点

constructor-arg 的使用如下所示,constructor-arg 节点类似于构造函数,bean 中必须要有相应的构造函数才可以使用,否则会报错。

1
2
3
4
<bean id="userDao" class="com.test.spring.dao.UserDao">
<constructor-arg index="0" value="Mysql"/>
<constructor-arg index="1" value="select 1"/>
</bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class UserDao {
/**
* 数据库类型
*/
private String dialect;
/**
* 数据库检测的语句
*/
private String pingSql;

public UserDao(String dialect, String pingSql) {
this.dialect = dialect;
this.pingSql = pingSql;
}
}

parseConstructorArgElement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* Parse a constructor-arg element.
* 解析构造函数属性
*/
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 1.提取基础属性index、type、name属性值
// 提取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 {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
} else {
try {
// 2.index不为空的处理
this.parseState.push(new ConstructorArgumentEntry(index));
// 2.1解析ele节点对应的属性值
Object value = parsePropertyValue(ele, bd, null);
// 2.2使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
// 2.3将type属性封装到ConstructorArgumentValues.ValueHolder
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
// 2.4将name属性封装到ConstructorArgumentValues.ValueHolder
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
// 2.5判断index是否重复指定, 如果是则抛出异常
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
} else {
// 将index和valueHolder以key-value形式添加至当前BeanDefinition的constructorArgumentValues
// 的indexedArgumentValues属性中,(用于上面判断index是否重复指定)
//将构造方法以及索引加入ConstructorArgumentValues 的容器
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 {
// 3.index为空的处理
this.parseState.push(new ConstructorArgumentEntry());
// 3.1解析ele节点对应的属性值
Object value = parsePropertyValue(ele, bd, null);
// 3.2使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
// 3.3将type属性封装到ConstructorArgumentValues.ValueHolder
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
// 3.4将name属性封装到ConstructorArgumentValues.ValueHolder
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
// 3.5将valueHolder添加至当前BeanDefinition的constructorArgumentValues的genericArgumentValues属性中
// 与上面的indexedArgumentValues类似,上面有index存为map,这边没index存为list
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
} finally {
this.parseState.pop();
}
}
}

1.首先拿到基础属性 index、type、name 的属性值。

2.index不为空的处理:

2.1 首先解析 ele 节点的值,可以看代码块4里的图,每个 constructor-arg 节点必然有一个属性值,可能是通过 value 属性、ref 属性、list 属性等。

2.5 判断index是否重复指定,如果是则抛出异常;如果不重复,则将 index 和 valueHolder 以 key-value 形式添加至当前BeanDefinition 的 constructorArgumentValues 的 indexedArgumentValues 属性中(用于前面判断index是否重复指定)。

3.index为空的处理。基本与2相同,不在赘述。

parsePropertyValue

解析属性元素的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");

// Should only have one child element: ref, value, list, etc.
// 1.拿到ele节点的子节点,例如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) {
// 只能有1个子节点,否则抛出异常
error(elementName + " must not contain more than one sub-element", ele);
} else {
// 找到子节点,赋值给subElement
subElement = (Element) node;
}
}
}
// 2.解析constructor-arg上的ref属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 3.解析constructor-arg上的value属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
// 4.合法性校验。在constructor-arg上:ref属性、value属性、子节点,三者只能有1个,因为这三个都是用来表示该节点的值。
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
// 5.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;
} else if (hasValueAttribute) {
// 6.value属性的处理,使用TypedStringValue封装
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
} else if (subElement != null) {
// 7.解析子节点
return parsePropertySubElement(subElement, bd);
} else {
// 8.既没有ref属性,也没有value属性,也没有子节点,没法获取ele节点的值,直接抛异常
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}

parsePropertySubElement

解析子元素属性的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
// 1.校验是否为默认的命名空间,如果不是则走解析自定义节点代码
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
// 2.解析bean节点
} else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
// 3.解析ref节点
} else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
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;
// 4.解析idref节点
} else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
// 5.解析value节点
} else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
// 6.解析null节点
} else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// It's a distinguished null value. Let's wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
// 7.解析array节点
} else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
// 8.解析list节点
} else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
// 9.解析set节点
} else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
// 10.解析map节点
} else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
// 11.解析props节点
} else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
} else {
// 12.未知属性,抛异常
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}

parseValueElement

解析Value的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Object parseValueElement(Element ele, @Nullable String defaultTypeName) {
// It's a literal value.
// 拿到ele节点值
String value = DomUtils.getTextValue(ele);
// 拿到ele节点的type属性
String specifiedTypeName = ele.getAttribute(TYPE_ATTRIBUTE);
String typeName = specifiedTypeName;
if (!StringUtils.hasText(typeName)) {
// ele节点没有type属性则则使用入参defaultTypeName
typeName = defaultTypeName;
}
try {
// 1.使用value和type构建TypedStringValue
TypedStringValue typedValue = buildTypedStringValue(value, typeName);
typedValue.setSource(extractSource(ele));
typedValue.setSpecifiedTypeName(specifiedTypeName);
return typedValue;
} catch (ClassNotFoundException ex) {
error("Type class [" + typeName + "] not found for <value> element", ele, ex);
return value;
}
}

buildTypedStringValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected TypedStringValue buildTypedStringValue(String value, @Nullable String targetTypeName)
throws ClassNotFoundException {

ClassLoader classLoader = this.readerContext.getBeanClassLoader();
TypedStringValue typedValue;
// 1.targetTypeName为空,则只使用value来构建TypedStringValue
if (!StringUtils.hasText(targetTypeName)) {
typedValue = new TypedStringValue(value);

// 2.targetTypeName不为空,并且classLoader不为null
} else if (classLoader != null) {
// 2.1 利用反射,构建出type的Class,如果type是基本类型,或者 java.lang 包下的常用类,
// 可以直接从缓存(primitiveTypeNameMap、commonClassCache)中获取
Class<?> targetType = ClassUtils.forName(targetTypeName, classLoader);
typedValue = new TypedStringValue(value, targetType);
} else {
typedValue = new TypedStringValue(value, targetTypeName);
}
return typedValue;
}

parseSetElement

解析集合元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public List<Object> parseListElement(Element collectionEle, @Nullable BeanDefinition bd) {
// 1.拿到collectionEle节点的value-type,顾名思义,该属性就是该list节点下的value的类型
String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
// 2.拿到collectionEle节点的所有子节点, 一般为<value>
NodeList nl = collectionEle.getChildNodes();
// 3.new一个ManagedList,用于存放字节点的值
ManagedList<Object> target = new ManagedList<>(nl.getLength());
target.setSource(extractSource(collectionEle));
target.setElementTypeName(defaultElementType);
target.setMergeEnabled(parseMergeAttribute(collectionEle));
// 4.解析子节点集合
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}

protected void parseCollectionElements(
NodeList elementNodes, Collection<Object> target, @Nullable BeanDefinition bd, String defaultElementType) {
// 4.1 遍历elementNodes
for (int i = 0; i < elementNodes.getLength(); i++) {
Node node = elementNodes.item(i);
// 4.2 跳过description节点
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
// 4.3 调用parsePropertySubElement方法对节点进行解析, 正常list的子节点为<value>节点会直接走到value节点的解析
// 如果list的子节点还是list,则相当于递归在走到此方法
target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
}
}
}

​ 正常情况下,list 里面应该是 value节点(见下图),则会走到代码块8解析出对应的value,然后结束。特殊情况下,可能 list 里面还是 list,则相当于递归在走到此方法。

parsePropertyElements

解析bean中的property标签,并封装到BeanDefinition的MutablePropertyValues属性中来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 解析bean中的property标签
*/
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
// 拿到beanEle节点的所有子节点
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);
}
}
}

​ 拿到 beanEle 节点的所有子节点,遍历解析所有是 property 节点的子节点,property 的使用如下图所示,property 节点类似于set方法,bean 中的属性必须要有 set 方法才可以使用,否则会报错。

1
2
3
4
5
6
7
8
9
10
<bean id="userDao" class="com.test.spring.dao.UserDao">
<property name="dialect" value="mysql"/>
<property name="pingSql" value="select 1"/>
<property name="dbList">
<list>
<value>db2</value>
<value>oracle</value>
</list>
</property>
</bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class UserDao {
/**
* 数据库类型
*/
private String dialect;
/**
* 数据库检测的语句
*/
private String pingSql;

private List<String> dbList;

public String getDialect() {
return dialect;
}

public void setDialect(String dialect) {
this.dialect = dialect;
}

public String getPingSql() {
return pingSql;
}

public void setPingSql(String pingSql) {
this.pingSql = pingSql;
}

public List<String> getDbList() {
return dbList;
}

public void setDbList(List<String> dbList) {
this.dbList = dbList;
}

@Override
public String toString() {
return "UserDao{" +
"dialect='" + dialect + '\'' +
", pingSql='" + pingSql + '\'' +
'}';
}
}

parsePropertyElement

解析bean中的property标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 解析bean中的property标签
*/
public void parsePropertyElement(Element ele, BeanDefinition bd) {
// 1.拿到name属性
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
// name属性为必要属性,如果没有配置,则抛出异常
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
// 2.校验在相同bean节点下,是否存在相同的name属性,如果存在则抛出异常
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
// 3.解析属性值
Object val = parsePropertyValue(ele, bd, propertyName);
// 4.将解析的属性值和属性name封装成PropertyValue
PropertyValue pv = new PropertyValue(propertyName, val);
// 5.解析meta节点(基本不用,不深入解析)
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
// 6.将解析出来的PropertyValue,添加到BeanDefinition的propertyValues属性中(上面的重复校验用到)
bd.getPropertyValues().addPropertyValue(pv);
} finally {
this.parseState.pop();
}
}
小结

这一部分是将默认标签解析成BeanDefinition的主要过程

  1. 获取元素的id属性设置给beanName属性
  2. 检查beanName是否存在
  3. 创建GenericBeanDefinition对象并初始化parentName,beanClass等信息
  4. 公共标签属性的解析,并把解析出来的属性设置到BeanDefinition对象中
  5. 解析lookup-method标签封装为LookupOverride添加到BeanDefinition的methodOverrides中
  6. 解析replaced-method标签封装为ReplaceOverride添加到BeanDefinition的methodOverrides中
  7. 解析constructor-arg标签封装为ConstructorArgumentValues添加到BeanDefinition的constructorArgumentValues中
  8. 解析property标签封装为PropertyValue添加到BeanDefinition的propertyValues中

decorateBeanDefinitionIfRequired

该方法功能不重要,设计模式重点看一下,装饰者设计模式,加上SPI设计思想

主要实现了命令标签的解析类似于 p:username=”Jack” p:password=”123” c:age=”12” c:sex=”1”标签的解析

decorateBeanDefinitionIfRequired

开始对<bean class=”xx” p:username=”yyy”/>标签的解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

BeanDefinitionHolder finalDefinition = originalDef;

//根据bean标签属性装饰BeanDefinitionHolder,比如<bean class="xx" p:username="yyy"/>
// 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;
}

核心方法是decorateIfRequired 方法使用了装饰者模式

decorateIfRequired

装饰者设计模式 实现了命名控制属性注入的

是spring的SPI机制,从spring.handlers获取对应的解析类进行操作 具体流程可以参考《命名空间解析》

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 装饰者设计模式 实现了命名控制属性注入的
*
*/
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

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

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

//调用NamespaceHandler处理类的decorate方法,开始具体装饰过程,并返回装饰完的对象
//org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
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/schema/")) {
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;
}
decorate

来到SimplePropertyNamespaceHandler类的decorate方法,进行装饰元素的解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
if (node instanceof Attr) {
Attr attr = (Attr) node;
//命名标签的名称 例如 p:username="123" 获取到的是 username
String propertyName = parserContext.getDelegate().getLocalName(attr);
//获取到属性的值
String propertyValue = attr.getValue();
//获取definition的MutablePropertyValues列表
MutablePropertyValues pvs = definition.getBeanDefinition().getPropertyValues();
//检查是否重复定义 如果重复就报错
if (pvs.contains(propertyName)) {
parserContext.getReaderContext().error("Property '" + propertyName + "' is already defined using " +
"both <property> and inline syntax. Only one approach may be used per property.", attr);
}
//如果是以 -ref 就是对象引用注入
if (propertyName.endsWith(REF_SUFFIX)) {
propertyName = propertyName.substring(0, propertyName.length() - REF_SUFFIX.length());
pvs.add(Conventions.attributeNameToPropertyName(propertyName), new RuntimeBeanReference(propertyValue));
} else {
//否则是普通属性注入
pvs.add(Conventions.attributeNameToPropertyName(propertyName), propertyValue);
}
}
return definition;
}

该方法是对解析bean的装饰对<bean class=”xx” p:username=”yyy”/>标签进行解析

小结

解析流程如下

  1. 获取装饰元素的名称 例如p:username 获取对应的名称 username
  2. 获取对应的value
  3. 检查属性名称是否有-ref,如果有说明是引用的其他bean元素 类似于<property name=”xx” ref=”yy”/>
    1. 首先去掉**-ref**字符
    2. 将其value转换成RuntimeBeanReference引用类型加入到MutablePropertyValues
  4. 如果不包含**-ref**,将属性的名称和值加入MutablePropertyValues

注册BeanDefinition

registerBeanDefinition

注册BeanDefinition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Register the given bean definition with the given bean factory.
* 注册BeanDefinition
*
* @param definitionHolder the bean definition including name and aliases
* @param registry the bean factory to register with
* @throws BeanDefinitionStoreException if registration failed
*/
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {

//1. 获取需要注册的beanName
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();

// 2.注册beanName、BeanDefinition到缓存中(核心逻辑),实现类为; DefaultListableBeanFactory
//完成BeanDefinition的注册,重点看,重要程度 5
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// 注册bean名称的别名(如果有的话)
//建立别名和 id的映射,这样就可以根据别名获取到id
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// 3.注册bean的beanName和对应的别名映射到缓存中(缓存:aliasMap)
registry.registerAlias(beanName, alias);
}
}
}

评论