# 1.解析BeanDefinition
|
|
作用就是将element封装进bdHolder里面.
其源码很长,不贴了,但功能流程还很清晰
处理id,name,aliases. 关键是需要beanName ,
name分隔开做aliases的list.
beanName优先用id,没id的话就从aliases里remove(0)出来一个,
如果有beanName了,校验beanName和aliases唯一性
如果还是没beanName,等会再给它用方法生成个
- 将element解析,放入GenericBeanDefinition里面
- 把上一步拿到的bd连带着aliases转String[], beanName一起封装一下成为BeanDefinitionHolder返回;如果上一步返回null,就直接返回null
这里的关键步骤显然是第二步,element转beanDefinition
|
|
这个方法也很长,大概步骤是
- this.parseState.push(new BeanEntry(beanName)) 开头push,结尾pop,不明白是做什么???
- 获取下className和parent
- 拿着className和parent去创建个GenericBeanDefinition bd
- 拿着bd和element进行一系列的解析步骤,把各种值塞入bd
- 返回bd
- this.parseState.pop();
# 解析各种属性
|
|
这个方法的代码丧心病狂地达到了70行,相比于目前读到的其它源码,确实是最长的一个.
但是里面的内容其实并不复杂,就是一个个地从element拿属性,塞属性进bd, 各种 get & set .感觉确实不太好重构.
解析了很多属性,包括scope,abstract,lazy-init,autowire…
# 解析子元素meta
# 解析子元素lookup-method
解析方法和meta差别很小,这里主要是去了解下lookup-method的功能
|
|
method是beanClass中的一个方法,beanClass和method是不是抽象都无所谓,不会影响CGLIB的动态代理,根据项目实际需求去定义。non-singleton-bean指的是lookup-method中bean属性指向的必须是一个非单例模式的bean,当然如果不是也不会报错,只是每次得到的都是相同引用的bean(同一个实例),这样用lookup-method就没有意义了。
参考: Spring - lookup-method方式实现依赖注入
# 解析子元素replaced-method
这个不仅可以动态地替换返回实体bean,而且还能动态地更改原有方法的逻辑!!!
# 解析子元素constructor-arg
这里提取构造参数的一些属性值就相比之前复杂多了.
- 提取index,type,name,判断是否有index属性值
- 如果有index:
- 构造Entry压入parseState栈
- 解析constructor-arg的子元素
- 使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
- 将index,type,name属性一并封装在ValueHolder中,
- 验证index是否用过了,用过则抛错,跳过此条参数
- ValueHolder添加至当前BeanDefinition的construArgumentValues的indexedArgurmentValues属性中.
- 弹出栈
- 如果没有index:
- 同上压入栈
- 解析constructor-arg的子元素
- 使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
- 将index,type,name属性一并封装在ValueHolder中,并且添加至当前BeanDefinition的construArgumentValues的genericArgurmentValues属性中.
- 弹出栈
可以看出,有没有index其实流程没区别,主要在于在bd中保存的位置不同,另外有index的需要验证下index的唯一
# parsePropertyValue
解析constructor-arg的子元素
|
|
这个方法也很长,里面的关键在于此处解析constructor-arg下面的子元素时,下面三种情况必须且只能占一种
- 有ref属性
- 有value属性
- 有子元素(description和meta除外,这两种不用处理),子元素只能列一个
上面三种情况对应不同的处理方法:
ref:
校验下ref的值不能为空,然后
1 2 3
RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(this.extractSource(ele)); return ref;
用RuntimeBeanReference封装下这个要引用的bean的beanName,塞下resource,返回
查看BeanReference源码时注意到有RuntimeBeanReference和RuntimeBeanNameReference两种实现,貌似区别不大,RuntimeBeanReference多个toParent的boolean属性,此处给的也是false值.
value:
1 2 3
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute("value")); valueHolder.setSource(this.extractSource(ele)); return valueHolder;
用TypedStringValue封装下value的值 返回
TypedStringValue和BeanReference都是实现BeanMetadataElement的,实现了Object getSource();方法
子元素parsePropertySubElement: 这个又复杂了,判断10+种情况
如果不是默认命名空间,则按其自定义的方式去解析
bean元素,按照本文的方式解析,拿到一个BeanDefinitionHolder bdHolder,bdHolder不为空的话,可能还要装饰一下(在这个元素的属性或子元素节点命名空间不为默认情况下, 用自定义方式解析后去装饰),返回最终解析+装饰的结果 bdHolder
ref: 主要还是拿要引用的beanName, 取值顺序为其属性 bean–local–parent,和之前的ref一样,用RuntimeBeanReference封装下这个要引用的bean的beanName,塞下resource,返回
idref:和ref差不多,取值顺序为bean–local
value: 拿文本值,拿type值,没type就用传来的默认typeName,此处为null,然后根据value,typeName和BeanClassLoader来构造一个TypedStringValue,返回
null:用null来new一个TypedStringValue,返回
array:
拿value-type和子节点list
用type和子节点size构建个ManagedArray
ManagedArray继承ManagedList
ManagedList实现Mergeable和BeanMetadataElement ,可以有merge和getSource
然后设置source和merge属性 (此处还多余重复了一遍setElementTypeName(elementType);),
然后 this.parseCollectionElements(nl, target, bd, defaultElementType); 对其子节点进行一个个的再解析子元素,还是调用parsePropertySubElement方法,只是带了个默认类型defaultElementType
看到这已经有点晕了, 最初的那个bd都跑了多少路了?? 子元素里的属性也往bd里直接塞,不就丢失父子结构了么???
list: 和array没什么差别,仅有的区别在于用ManagedList ,(ManagedArray还是继承自他的)
set:同上,仅有的区别在于用ManagedSet,继承自LinkedHashSet
map:这个方法100行,非常长
1 2 3 4 5 6
<constructor-arg name="score"> <map> <entry key="math" value="90"/> <entry key="english" value="85"/> </map> </constructor-arg>
拿key-tpye和value-type,子节点entry的list
构建ManagedMap,设source,KeyTypeName,ValueTypeName,MergeEnable
遍历entry节点
拿entry再下一层的子节点,遍历,如果是key节点,则设这个entry的keyEle为此,key只能最多出现一遍, 然后剩下的节点除了description之外最多只能有一个,作为valueEle.所以此处表明这里貌似可以这么写???
1 2 3 4 5 6 7 8 9 10
<constructor-arg name="score"> <map> <entry > <key>哈哈</key> <vvvaaluue>这样写value??</vvvaaluue> <description>描述随便写,相当于注释</description> <description>描述随便写,相当于注释</description> </entry> </map> </constructor-arg>
然后又和解析constructor-arg的子元素类似的情况,三种情况必须且只能出现一种:1.“key"属性;2.“key-ref"属性;3.“key"子元素, 三种对应不同的解析
key:TypedStringValue来封装
key-ref:RuntimeBeanReference来封装
key子元素: 里面必须且只能有一个节点,进行parsePropertySubElement,这和在array那看到的一样了
三种解析后返回的Objext赋值给key
然后解析value,和上面解析key类似,以下三种必须且只能出现一种:
(有点区别在于value-ref属性和value-type属性不能并存,
有value-type时就必须要有value属性或者value子节点)
value属性:拿value-type,没有就用默认的value-type(map节点下的,当然也可能没有),构建TypedStringValue
value-ref属性:构建RuntimeBeanReference
value子节点:老样子,还是parsePropertySubElement这样来继续解析子节点
key,value都拿到之后,会塞入ManagedMap,最终返回ManagedMap
props:
1 2 3 4 5 6 7 8 9 10
<constructor-arg> <props> <prop key="firstName"> Rob </prop> <prop key="secondName"> Harrop </prop> </props> </constructor-arg>
这个就简单了,无非就是遍历props下面的prop元素,拿key属性,拿文本值的trim(),塞入Properties对象,返回这个properties
至此,解析constructor-arg的子元素算是完成了, 都快忘掉这只是解析constructor-arg里的第一步了…
总之这一步解析完子元素,返回Object对象回来,作为value
# ValueHolder
用上一步返回回来的value,构造个ValueHolder
ValueHolder类在ConstructorArgumentValues类里,实现了BeanMetadataElement,属性不多,就value,type,name,source,converted,convertedValue
然后constructor-arg有type属性就给vh塞type,有name塞name, 塞source
校验完index唯一之后
|
|
构造器参数塞入bd的constructorArgumentValues,后面无index的处理方式和这里有index的主要区别就是在往这个constructorArgumentValues里面塞的位置不同.
BeanDefinition里定义了
|
|
这个接口,在AbstractBeanDefinition里有
|
|
目测专门存放构造器的参数值的
下面我们来熟悉一下ConstructorArgumentValues这个类吧..
(这个类实在太麻烦了,写着写着2000多字,重开一文了,即下一篇笔记)
# 解析子元素property
- 拿name属性并且校验
- parseState.push()
- 校验name在bd.getPropertyValues()里面唯一
- parsePropertyValue 提取出property里面的子元素或者ref或者value,这个步骤和之前的解析子元素constructor-arg里面的parsePropertyValue是同一个方法! 只是这里调用带上了propertyName,这里的name是不可能为空的 ,因为第一步校验了
- 拿第4步的结果val和propertyName封装一个PropertyValue ,这个也是继承BeanMetadataAttributeAccessor的,
- 然后和之前的解析子元素meta一模一样来解析这里面的meta子元素
- setSource
- propertyValue塞入bd
- parseState.pop()
# 解析子元素Qualifier
Qualifier通常都是以注解形式使用的,用于在注入bean时明确指明Bean的名称
拿type属性并校验
parseState.push()
1
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
setSource
拿value属性,如果有的话就塞入qualifier
遍历子节点,取其name和value(两者都必须存在),new BeanMetadataAttribute ,塞入qualifier
qualifier塞入bd
parseState.pop()
# bd设好resource和source,作为结果成功返回.
# 2.生成beanName
如果这个bean之前没有id或name,那就为它生成个beanName
其规则也略烦,不过感觉不重要,简单带过一下,有一种生成规则如下
命名主要根据的属性有
getBeanClassName
父bean名+$child
工厂名+$created
‘class’ nor ‘parent’ nor ‘factory-bean’
后缀再加上#
如果是内部bean再就加bd生成的hexString
如果不是,就再加registry里这种bean的序号 从0开始
# 3.返回BeanDefinitionHolder
|
|