继续spring源码的学习之路,现在越来越觉得这个真的很枯燥的,而且我觉得要是自己来看源码,真的看不下去,不是没有耐心,而是真的没有头绪,我觉得结合着书来看,还是很有必要的,最起码大致的流程是能够捋清楚的,继续,希望自己能够坚持到最后!
一、标签解析的总体的理解
spring标签包括默认标签和自定义标签两种,今天只是来探索默认标签的!
org.springframework.beans.factory.xml包下的DefaultBeanDefinitionDocumentReader类中
1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
2 // 对import标签的处理
3 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
4 importBeanDefinitionResource(ele);
5 }
6 // 对alias标签的处理
7 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
8 processAliasRegistration(ele);
9 }
10 // 对bean标签的处理
11 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
12 processBeanDefinition(ele, delegate);
13 }
14 // 对beans标签的处理
15 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
16 // recurse
17 doRegisterBeanDefinitions(ele);
18 }
19 }
二、bean标签的解析与注册
org.springframework.beans.factory.xml包下的DefaultBeanDefinitionDocumentReader类中
1 /**
2 * Process the given bean element, parsing the bean definition
3 * and registering it with the registry.
4 */
5 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
6 // 进行元素解析,获取配置文件中的属性
7 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
8 if (bdHolder != null) {
9 // 对子节点下自定义标签进行解析
10 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
11 try {
12 // Register the final decorated instance.
13 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
14 }
15 catch (BeanDefinitionStoreException ex) {
16 getReaderContext().error("Failed to register bean definition with name '" +
17 bdHolder.getBeanName() + "'", ele, ex);
18 }
19 // Send registration event.
20 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
21 }
22 }
(1)首先委托BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder,
经过这份方法之后,bdHolder实例已经包含我们配置文件中的各种配置属性了,例如class、name、id、alias之类的属性
(2)当返回的bdHolder不为空的情况下,若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析
(3)解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils类的registerBeanDefinition方法
(4)最后发出响应事件,通知相关的监听器,这个bean已经加载完成
1、解析BeanDefinition(Definition的意思是定义 实体bean的定义)
这个对应着就是元素解析及信息提取,也就是下面这行代码的解析,进入BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
BeanDefinitionParserDelegate类所在的包:org.springframework.beans.factory.xml下BeanDefinitionParserDelegate类
第一步调用此方法:
1 /**
2 * Parses the supplied {@code <bean>} element. May return {@code null}
3 * if there were errors during parse. Errors are reported to the
4 * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
5 */
6 @Nullable
7 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
8 return parseBeanDefinitionElement(ele, null);
9 }
下一步调用具体的代码实现:
1 /**
2 * Parses the supplied {@code <bean>} element. May return {@code null}
3 * if there were errors during parse. Errors are reported to the
4 * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
5 */
6 @Nullable
7 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
8 // 解析id属性
9 String id = ele.getAttribute(ID_ATTRIBUTE);
10 // 解析name属性
11 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
12
13 // 分割name属性 注意这里为什么进行name属性的分割,因为bean配置文件的定义中,name是可以有多个值的,多个值之前用","":"隔开
14 List<String> aliases = new ArrayList<>();
15 if (StringUtils.hasLength(nameAttr)) {
16 // MULTI_VALUE_ATTRIBUTE_DELIMITERS变量的定义就是:MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";
17 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
18 // aliases的意思是别名
19 aliases.addAll(Arrays.asList(nameArr));
20 }
21
22 String beanName = id;
23 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
24 beanName = aliases.remove(0);
25 if (logger.isDebugEnabled()) {
26 logger.debug("No XML 'id' specified - using '" + beanName +
27 "' as bean name and " + aliases + " as aliases");
28 }
29 }
30
31 if (containingBean == null) {
32 checkNameUniqueness(beanName, aliases, ele);
33 }
34
35 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
36 if (beanDefinition != null) {
37 if (!StringUtils.hasText(beanName)) {
38 try {
39 // 如果不存在beanName那么根据spring中提供的命名规则为当前bean生成对应的beanName
40 if (containingBean != null) {
41 beanName = BeanDefinitionReaderUtils.generateBeanName(
42 beanDefinition, this.readerContext.getRegistry(), true);
43 }
44 else {
45 beanName = this.readerContext.generateBeanName(beanDefinition);
46 // Register an alias for the plain bean class name, if still possible,
47 // if the generator returned the class name plus a suffix.
48 // This is expected for Spring 1.2/2.0 backwards compatibility.
49 String beanClassName = beanDefinition.getBeanClassName();
50 if (beanClassName != null &&
51 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
52 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
53 aliases.add(beanClassName);
54 }
55 }
56 if (logger.isDebugEnabled()) {
57 logger.debug("Neither XML 'id' nor 'name' specified - " +
58 "using generated bean name [" + beanName + "]");
59 }
60 }
61 catch (Exception ex) {
62 error(ex.getMessage(), ele);
63 return null;
64 }
65 }
66 String[] aliasesArray = StringUtils.toStringArray(aliases);
67 return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
68 }
69
70 return null;
71 }
以上就是对默认标签解析的全过程,尽管只能看到对id以及name属性的解析,但是大致的思路我们是知道了,
在开始对属性展开全面解析前,spring在外层又做了一个当前层的功能架构,在当前层完成的主要工作如下:
(1)提取元素中的id和name属性
(2)进一步解析其他所有属性并统一封装到GenericBeanDefinition中
(3)如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanName
(4)将获取到的信息封装到BeanDefinitionHolder的实例中
我们进一步查看步骤2中对标签其他属性的解析过程:
这个方法还是在BeanDefinitionParserDelegate类中
1 /**
2 * Parse the bean definition itself, without regard to name or aliases. May return
3 * {@code null} if problems occurred during the parsing of the bean definition.
4 */
5 @Nullable
6 public AbstractBeanDefinition parseBeanDefinitionElement(
7 Element ele, String beanName, @Nullable BeanDefinition containingBean) {
8
9 this.parseState.push(new BeanEntry(beanName));
10
11 String className = null;
12 // 对class属性的解析
13 if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
14 className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
15 }
16 String parent = null;
17 // 对parent属性解析
18 if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
19 parent = ele.getAttribute(PARENT_ATTRIBUTE);
20 }
21
22 try {
23 // 创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition,需要到createBeanDefinition方法中看一下源码
24 AbstractBeanDefinition bd = createBeanDefinition(className, parent);
25
26 // 硬编码解析默认bean的各种属性
27 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
28 // 提取description
29 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
30
31 // 解析元数据
32 parseMetaElements(ele, bd);
33 // 解析lookup-method属性
34 parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
35 // 解析replaced-method属性
36 parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
37 /**
38 * lookup-method通常称为获取器注入,spring in action中对它的描述是,一种特殊的方法注入,<br>
39 * 它是把一个方法声明为返回某种类型的bean,而实际要返回的bean是在配置文件里面配置的, <br>
40 * 可用在设计可插拔的功能上,接触程序依赖。同理replaced-method则是将之前执行的逻辑替换掉
41 */
42
43 // 解析构造函数参数
44 parseConstructorArgElements(ele, bd);
45 // 解析property子元素
46 parsePropertyElements(ele, bd);
47 // 解析qualifier子元素
48 parseQualifierElements(ele, bd);
49
50 bd.setResource(this.readerContext.getResource());
51 bd.setSource(extractSource(ele));
52
53 return bd;
54 }
55 catch (ClassNotFoundException ex) {
56 error("Bean class [" + className + "] not found", ele, ex);
57 }
58 catch (NoClassDefFoundError err) {
59 error("Class that bean class [" + className + "] depends on not found", ele, err);
60 }
61 catch (Throwable ex) {
62 error("Unexpected failure during bean definition parsing", ele, ex);
63 }
64 finally {
65 this.parseState.pop();
66 }
67
68 return null;
69 }
终于,bean标签的所有属性,不论常用的还是不常用的我们都看到了,尽管有些复杂的属性还需要进一步解析,继续一些复杂标签的属性的解析
(1)创建用于属性承载的BeanDefinition
BeanDefinition是一个接口,在spring中有三种实现,RootBeanDefinition、ChildBeanDefinition以及GenericBeanDefinition,
三种实现均继承了AbstractBeanDefinition,其中BeanDefinition是配置文件<bean>元素标签在容器内部的表现形式。
BeanDefinition属性和<bean>标签中属性是一一对应的,RootBeanDefinition是最常用的实现类,他对应一般元素的<bean>标签
spring通过BeanDefinition将配置文件中的<bean>配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。
spring容器的BeanDefinitionRegistry就像是spring配置信息的内存数据库,主要以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息
要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例,createBeanDefinition(className, parent)就是创建这个实例
1 /**
2 * Create a new GenericBeanDefinition for the given parent name and class name,
3 * eagerly loading the bean class if a ClassLoader has been specified.
4 * @param parentName the name of the parent bean, if any
5 * @param className the name of the bean class, if any
6 * @param classLoader the ClassLoader to use for loading bean classes
7 * (can be {@code null} to just register bean classes by name)
8 * @return the bean definition
9 * @throws ClassNotFoundException if the bean class could not be loaded
10 */
11 public static AbstractBeanDefinition createBeanDefinition(
12 @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
13
14 GenericBeanDefinition bd = new GenericBeanDefinition();
15 bd.setParentName(parentName);
16 if (className != null) {
17 if (classLoader != null) {
18 bd.setBeanClass(ClassUtils.forName(className, classLoader));
19 }
20 else {
21 bd.setBeanClassName(className);
22 }
23 }
24 return bd;
25 }
(2)解析各种属性
当创建了bean信息的承载实例后,便可以进行bean信息的各种属性解析了,首先我们进入parseBeanDefinitionAttributes方法,这个是对bean标签中所有属性的解析
此方法在org.springframework.beans.factory.xml包下的BeanDefinitionParserDelegate类中:
1 /**
2 * Apply the attributes of the given bean element to the given bean * definition.
3 * @param ele bean declaration element
4 * @param beanName bean name
5 * @param containingBean containing bean definition
6 * @return a bean definition initialized according to the bean element attributes
7 */
8 public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
9 @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
10 // 解析singleton属性
11 if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
12 error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
13 }
14 // 解析scope属性
15 else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
16 bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
17 }
18 else if (containingBean != null) {
19 // Take default from containing bean in case of an inner bean definition.
20 bd.setScope(containingBean.getScope());
21 }
22
23 // 解析abstract属性
24 if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
25 bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
26 }
27
28 // 解析lazy-init属性
29 String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
30 if (isDefaultValue(lazyInit)) {
31 lazyInit = this.defaults.getLazyInit();
32 }
33 // 若没有设置或设置成其他字符都设置成false
34 bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
35
36 // 解析autowire属性
37 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
38 bd.setAutowireMode(getAutowireMode(autowire));
39
40 // 解析depends-on属性
41 if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
42 String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
43 bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
44 }
45
46 // 解析autowire-candidate属性
47 String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
48 if (isDefaultValue(autowireCandidate)) {
49 String candidatePattern = this.defaults.getAutowireCandidates();
50 if (candidatePattern != null) {
51 String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
52 bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
53 }
54 }
55 else {
56 bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
57 }
58
59 // 解析primary属性
60 if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
61 bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
62 }
63
64 // 解析init-method属性
65 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
66 String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
67 bd.setInitMethodName(initMethodName);
68 }
69 else if (this.defaults.getInitMethod() != null) {
70 bd.setInitMethodName(this.defaults.getInitMethod());
71 bd.setEnforceInitMethod(false);
72 }
73
74 // 解析destroy-method属性
75 if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
76 String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
77 bd.setDestroyMethodName(destroyMethodName);
78 }
79 else if (this.defaults.getDestroyMethod() != null) {
80 bd.setDestroyMethodName(this.defaults.getDestroyMethod());
81 bd.setEnforceDestroyMethod(false);
82 }
83
84 // 解析factory-method属性
85 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
86 bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
87 }
88 // 解析factory-bean属性
89 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
90 bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
91 }
92
93 return bd;
94 }
(3)解析子元素meta
这个属性之前都没有听说过,更没有用过:,不过这里也把该属性的源代码拿出来,方便以后来学习:
具体xml中使用:
<bean id="myTestBean" class="bean.myTestBean">
<meta key="testStr" value="aaa" />
</bean>
meta属性解析方法的源代码:
此方法在org.springframework.beans.factory.xml包下的BeanDefinitionParserDelegate类中
1 public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
2 // 获取当前节点的所有子节点
3 NodeList nl = ele.getChildNodes();
4 for (int i = 0; i < nl.getLength(); i++) {
5 Node node = nl.item(i);
6 // 提取meta属性
7 if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
8 Element metaElement = (Element) node;
9 String key = metaElement.getAttribute(KEY_ATTRIBUTE);
10 String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
11 // 使用key、value构建BeanMetadataAttribute类
12 BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
13 attribute.setSource(extractSource(metaElement));
14 // 记录信息
15 attributeAccessor.addMetadataAttribute(attribute);
16 }
17 }
18 }
(4)解析子元素lookup-method
这个子元素也是不是很常用,所以就简单的记录一下,这个子元素通常称为获取器注入,spring in action 中有对这哥获取器注入的解释:
获取器注入是一种特殊的方法注入,他是把一个方法声明为返回某种类型的bean,但是实际要返回的bean是在配置文件中配置的,此方法
可用在设计有些可插拔的功能上,解除程序依赖
具体xml中使用:
<bean id="getBeanTest" class="test.lookup.bean.GetBeanTest">
<lookup-method name="getBean" bean="teacher"/>
</bean>
<bean id="teacher" class="test.lookup.bean.Teacher">
</bean>
lookup-method属性解析的源码:
此方法在org.springframework.beans.factory.xml包下的BeanDefinitionParserDelegate类中
1 /**
2 * Parse lookup-override sub-elements of the given bean element.
3 */
4 public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
5 NodeList nl = beanEle.getChildNodes();
6 for (int i = 0; i < nl.getLength(); i++) {
7 Node node = nl.item(i);
8 // 仅当在spring默认bean的子元素下且为lookup-method时有效
9 if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
10 Element ele = (Element) node;
11 // 获取要修饰的方法
12 String methodName = ele.getAttribute(NAME_ATTRIBUTE);
13 // 获取配置返回的bean
14 String beanRef = ele.getAttribute(BEAN_ELEMENT);
15 LookupOverride override = new LookupOverride(methodName, beanRef);
16 override.setSource(extractSource(ele));
17 overrides.addOverride(override);
18 }
19 }
20 }
(5)解析子元素replace-method
bean子元素-方法替换,可以在运行时用新的方法替换原来的方法。与之前lookup-method不同的是,replace-method不但可以
动态地替换返回实体bean,而且还能动态的更改原有方法的逻辑
具体xml 中使用:
<bean id="testChangeMethod" class="test.replacemethod.TestChangeMethod">
<replace-method name="changeMe" replacer="replacer" />
</bean>
<bean id="replacer" class="test.replacemethod.TestMethodReplacer" />
replace-method属性解析的源码:
此方法在org.springframework.beans.factory.xml包下的BeanDefinitionParserDelegate类中
1 /**
2 * Parse replaced-method sub-elements of the given bean element.
3 */
4 public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
5 NodeList nl = beanEle.getChildNodes();
6 for (int i = 0; i < nl.getLength(); i++) {
7 Node node = nl.item(i);
8 if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
9 Element replacedMethodEle = (Element) node;
10 // 提取要替换的旧的方法
11 String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
12 // 提取新的方法
13 String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
14 ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
15 // Look for arg-type match elements. 查找新方法对应的bean
16 List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
17 for (Element argTypeEle : argTypeEles) {
18 // 记录参数
19 String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
20 match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
21 if (StringUtils.hasText(match)) {
22 replaceOverride.addTypeIdentifier(match);
23 }
24 }
25 replaceOverride.setSource(extractSource(replacedMethodEle));
26 overrides.addOverride(replaceOverride);
27 }
28 }
29 }
无论是lookup-method还是replace-method都构建了一个MethodOverride,并最终记录在了AbstractBeanDefinition的MethodOverrides属性中了,
而这个属性如何完成它所提供的功能我们会在后面介绍
(6)解析子元素constructor-arg
这个就是比较熟悉的了,对构造函数的解析,这个是经常使用的,
<bean id="helloWorld" class="com.HelloWorld">
<constructor-arg index="0">
<value>java</value>
</constructor-arg>
<constructor-arg index="1">
<value>javascript</value>
</constructor-arg>
</bean>
这就是spring中构造函数最基础的配置,实现的功能是对HelloWorld bean的自动寻找对应的构造函数,并在初始化的过程中将参数传递进去
看一下constructor-arg的解析过程:spring通过parseConstructorArgElements方法解析的
此方法在org.springframework.beans.factory.xml包下的BeanDefinitionParserDelegate类中
1 /**
2 * Parse constructor-arg sub-elements of the given bean element.
3 */
4 public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
5 NodeList nl = beanEle.getChildNodes();
6 for (int i = 0; i < nl.getLength(); i++) {
7 Node node = nl.item(i);
8 if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
9 // 解析constructor-arg子元素
10 parseConstructorArgElement((Element) node, bd);
11 }
12 }
13 }
具体的实现逻辑是在另一个方法中实现的:
1 /**
2 * Parse a constructor-arg element.
3 */
4 public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
5 // 提取index属性
6 String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
7 // 提取type属性
8 String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
9 // 提取name属性
10 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
11 if (StringUtils.hasLength(indexAttr)) {
12 try {
13 int index = Integer.parseInt(indexAttr);
14 if (index < 0) {
15 error("'index' cannot be lower than 0", ele);
16 }
17 else {
18 try {
19 this.parseState.push(new ConstructorArgumentEntry(index));
20 // 解析ele对应的元素
21 Object value = parsePropertyValue(ele, bd, null);
22 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
23 if (StringUtils.hasLength(typeAttr)) {
24 valueHolder.setType(typeAttr);
25 }
26 if (StringUtils.hasLength(nameAttr)) {
27 valueHolder.setName(nameAttr);
28 }
29 valueHolder.setSource(extractSource(ele));
30 // 不允许指定相同的参数
31 if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
32 error("Ambiguous constructor-arg entries for index " + index, ele);
33 }
34 else {
35 bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
36 }
37 }
38 finally {
39 this.parseState.pop();
40 }
41 }
42 }
43 catch (NumberFormatException ex) {
44 error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
45 }
46 }
47 else {
48 // 没有index属性则忽略去属性,自动寻找
49 try {
50 this.parseState.push(new ConstructorArgumentEntry());
51 Object value = parsePropertyValue(ele, bd, null);
52 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
53 if (StringUtils.hasLength(typeAttr)) {
54 valueHolder.setType(typeAttr);
55 }
56 if (StringUtils.hasLength(nameAttr)) {
57 valueHolder.setName(nameAttr);
58 }
59 valueHolder.setSource(extractSource(ele));
60 bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
61 }
62 finally {
63 this.parseState.pop();
64 }
65 }
66 }
这个主要是分为两种情况:一种是指定了index属性,一种是没指定
如果配置中指定了index属性,则是这样解析
(1)解析constructor-arg的子元素
(2)使用ConstructorArgumentValues.ValueHolder 来封装解析出来的元素
(3)将index,name,type属性一并封装在ConstructorArgumentValues.ValueHolder 中,并添加到
BeanDefinition的ConstructorArgumentValues的indexedArgumentValue属性中
如果没有指定index属性,则如下解析方式
(1)解析constructor-arg的子元素
(2)使用ConstructorArgumentValues.ValueHolder 来封装解析出来的元素
(3)将index,name,type属性一并封装在ConstructorArgumentValues.ValueHolder 中,并添加到
BeanDefinition的ConstructorArgumentValues的genericArgumentValue属性中
这两种解析方式,关键在于属性存放的位置是不同的
我们尝试着进步了解解析构造函数配置中子元素的过程,进入parsePropertyValue方法
此方法在org.springframework.beans.factory.xml包下的BeanDefinitionParserDelegate类中
1 /**
2 * Get the value of a property element. May be a list etc.
3 * Also used for constructor arguments, "propertyName" being null in this case.
4 */
5 @Nullable
6 public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
7 String elementName = (propertyName != null ?
8 "<property> element for property '" + propertyName + "'" :
9 "<constructor-arg> element");
10
11 // Should only have one child element: ref, value, list, etc.
12 // 一个属性只能对应一种类型,ref、value、list等
13 NodeList nl = ele.getChildNodes();
14 Element subElement = null;
15 for (int i = 0; i < nl.getLength(); i++) {
16 Node node = nl.item(i);
17 // 对应的description或者meta属性不处理
18 if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
19 !nodeNameEquals(node, META_ELEMENT)) {
20 // Child element is what we're looking for.
21 if (subElement != null) {
22 error(elementName + " must not contain more than one sub-element", ele);
23 }
24 else {
25 subElement = (Element) node;
26 }
27 }
28 }
29
30 // 解析constructor-arg标签上的ref属性
31 boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
32 // 解析constructor-arg标签上的value属性
33 boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
34 /**
35 * 在constructor-arg上不存在
36 * 1、同时既有ref属性又有value属性
37 * 2、存在ref属性或者value属性且又有子元素
38 */
39 if ((hasRefAttribute && hasValueAttribute) ||
40 ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
41 error(elementName +
42 " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
43 }
44
45 if (hasRefAttribute) {
46 // ref属性的处理 使用RuntimeBeanReference封装对应的ref名称
47 String refName = ele.getAttribute(REF_ATTRIBUTE);
48 if (!StringUtils.hasText(refName)) {
49 error(elementName + " contains empty 'ref' attribute", ele);
50 }
51 RuntimeBeanReference ref = new RuntimeBeanReference(refName);
52 ref.setSource(extractSource(ele));
53 return ref;
54 }
55 else if (hasValueAttribute) {
56 // value属性的处理,使用TypedStringValue封装
57 TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
58 valueHolder.setSource(extractSource(ele));
59 return valueHolder;
60 }
61 else if (subElement != null) {
62 // 解析子元素
63 return parsePropertySubElement(subElement, bd);
64 }
65 else {
66 // Neither child element nor "ref" or "value" attribute found.
67 // 既没有ref 又没有value属性 也没有子元素 spring懵逼了
68 error(elementName + " must specify a ref or value", ele);
69 return null;
70 }
71 }
文字解释一下其中代码做了哪些事情:
(1)略过description或者meta
(2)提取constructor-arg上的ref和value属性,以便于根据规则验证正确性,其中以下情况不存在:
同时既有ref属性又有value属性
存在ref属性或者value属性且又有子元素
(3)ref属性的处理,使用RuntimeBeanReference封装对应的ref名称
<constructor-arg ref="a" />
(4)value属性的处理,使用TypedStringValue封装对应的value名称
<constructor-arg value="a" />
(5)子元素的处理
<constructor-arg>
<map>
<entry key="key" value="value"/>
</map>
<constructor-arg/>
看看如何解析各种子元素的,源码在parsePropertySubElement方法中
此方法在org.springframework.beans.factory.xml包下的BeanDefinitionParserDelegate类中
1 @Nullable
2 public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
3 return parsePropertySubElement(ele, bd, null);
4 }
进入return中的parsePropertySubElement方法
此方法在org.springframework.beans.factory.xml包下的BeanDefinitionParserDelegate类中
1 /**
2 * Parse a value, ref or collection sub-element of a property or
3 * constructor-arg element.
4 * @param ele subelement of property element; we don't know which yet
5 * @param defaultValueType the default type (class name) for any
6 * {@code <value>} tag that might be created
7 */
8 @Nullable
9 public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
10 if (!isDefaultNamespace(ele)) {
11 return parseNestedCustomElement(ele, bd);
12 }
13 else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
14 BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
15 if (nestedBd != null) {
16 nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
17 }
18 return nestedBd;
19 }
20 else if (nodeNameEquals(ele, REF_ELEMENT)) {
21 // A generic reference to any name of any bean.
22 String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
23 boolean toParent = false;
24 if (!StringUtils.hasLength(refName)) {
25 // A reference to the id of another bean in a parent context.
26 // 解析parent属性
27 refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
28 toParent = true;
29 if (!StringUtils.hasLength(refName)) {
30 error("'bean' or 'parent' is required for <ref> element", ele);
31 return null;
32 }
33 }
34 if (!StringUtils.hasText(refName)) {
35 error("<ref> element contains empty target attribute", ele);
36 return null;
37 }
38 RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
39 ref.setSource(extractSource(ele));
40 return ref;
41 }
42 // 对idref元素的解析
43 else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
44 return parseIdRefElement(ele);
45 }
46 // 对value子元素的解析
47 else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
48 return parseValueElement(ele, defaultValueType);
49 }
50 // 对null子元素的解析
51 else if (nodeNameEquals(ele, NULL_ELEMENT)) {
52 // It's a distinguished null value. Let's wrap it in a TypedStringValue
53 // object in order to preserve the source location.
54 TypedStringValue nullHolder = new TypedStringValue(null);
55 nullHolder.setSource(extractSource(ele));
56 return nullHolder;
57 }
58 // 对array子元素的解析
59 else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
60 return parseArrayElement(ele, bd);
61 }
62 // 对list子元素的解析
63 else if (nodeNameEquals(ele, LIST_ELEMENT)) {
64 return parseListElement(ele, bd);
65 }
66 // 对set子元素的解析
67 else if (nodeNameEquals(ele, SET_ELEMENT)) {
68 return parseSetElement(ele, bd);
69 }
70 // 对map子元素的解析
71 else if (nodeNameEquals(ele, MAP_ELEMENT)) {
72 return parseMapElement(ele, bd);
73 }
74 // 对props子元素的解析
75 else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
76 return parsePropsElement(ele);
77 }
78 // 这些子元素除外的就不处理了,报错!!!
79 else {
80 error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
81 return null;
82 }
83 }
(7)解析子元素property
这个就是提取bean标签中的属性定义,源码在parsePropertyElements方法中
此方法在org.springframework.beans.factory.xml包下的BeanDefinitionParserDelegate类中
1 /**
2 * Parse property sub-elements of the given bean element.
3 */
4 public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
5 NodeList nl = beanEle.getChildNodes();
6 for (int i = 0; i < nl.getLength(); i++) {
7 Node node = nl.item(i);
8 if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
9 parsePropertyElement((Element) node, bd);
10 }
11 }
12 }
13
14 /**
15 * Parse a property element.
16 */
17 public void parsePropertyElement(Element ele, BeanDefinition bd) {
18 // 获取配置配置元素中的name值
19 String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
20 if (!StringUtils.hasLength(propertyName)) {
21 error("Tag 'property' must have a 'name' attribute", ele);
22 return;
23 }
24 this.parseState.push(new PropertyEntry(propertyName));
25 try {
26 // 不允许多次对同一属性配置
27 if (bd.getPropertyValues().contains(propertyName)) {
28 error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
29 return;
30 }
31 Object val = parsePropertyValue(ele, bd, propertyName);
32 PropertyValue pv = new PropertyValue(propertyName, val);
33 parseMetaElements(ele, pv);
34 pv.setSource(extractSource(ele));
35 bd.getPropertyValues().addPropertyValue(pv);
36 }
37 finally {
38 this.parseState.pop();
39 }
40 }
可以看到上面函数与构造函数注入方式不同的是将返回值使用PropertyValue进行封装,并记录在BeanDefinition的PropertyValues属性中
(8)解析子元素qualifier
这个完全没有使用过,对于qualifier元素的获取,我们接触的最多的是注解的方式,在使用spring框架自动注入时,
spring容器中匹配的候选bean的数目必须有且仅有一个,当找不到一个匹配的bean的时候,spring框架将抛出 BeanCreationException,
并指出必须至少拥有一个指定的bean。
spring允许我们通过Qualifier指定注入bean的名称,配置如下:
<bean id="myTestBean" class="bean.MyTestBean">
<qualifier type="org.springframework.beans.factory.annotation.Qualifier" value="qf"/>
</bean>
解析过程大致相同!
来源:https://www.cnblogs.com/ssh-html/p/11179860.html