tinySpring 分析笔记——IOC(2)
学习项目 Github 地址:code4craft/tiny-spring
上一篇博客:tiny-spring分析笔记(1)
step-4-config-beanfactory-with-xml
- 在 step 3 中,“text”参数是直接写入 java 类中。
- step 4 的目标就是将参数的配置信息写入 xml 文件中,然后代码通过读取 xml 文件获得参数。
- 参数放入
BeanDefinition
中,然后用BeanFactory
将它们一一注册(包括 setBean),最终实现 getBean。
4.1 tinyioc.xml
1 | // 部分代码: |
4.2 BeanFactoryTest.java
1 | public class BeanFactoryTest { |
详细步骤:
- 首先 new 一个
XmlBeanDefinitionReader
类,调用其父类 BeanDefinitionReader 的构造方法,在构造方法中,new 一个HashMap<String,BeanDefinition>
和一个ResourceLoader
,分别赋给其私有属性registry
和resourceLoader
。 - 调用
XmlBeanDefinitionReader
的loadBeanDefinitions
方法并传入 xml 文件的文件名。 - 随后将 xml 文件转换成 URL 类型,再将 url 与资源建立联系(使用 URLConnection 类),得到
InputStream
对象。 - 随后使用
DocumentBuilder
(DocumentBuilder 实例也是用 DocumentbuilderFactory 的生成的工厂实例)解析inputStream
,得到Document
对象。 - 将
document
沿着根元素解析,new 一个BeanDefinition
(如果有多个 name-class 属性,将对应多个BeanDefinition
,以下以一个 name-class 属性为例),拿到 xml 所配置的name
和value
值并组装成① PropertyValues
,之后拿到② beanClassName
和③ beanClass
,把它们放入beanDefinition
中。 - 然后 new 一个
BeanFactory
(引用其实现类 AutowireCapableBeanFactory),生成④ bean
实例,放入beanDefinition
中(至此,4项凑齐),同时将beanDefinition
注册进属性beanDefinitionMap
中。 - 后续需要调用
helloWorldService.helloWorld()
时,只需要从beanFactory
中getBean("helloWorldService")
,即可。 - 完毕。
特点:
- 完成了读取 xml 文件的 I/O 操作;
- 考虑了配置文件中多个 class 的情况;
- 但没有考虑多个 bean 循环依赖的问题。
step-5-inject-bean-to-bean
- step 4 的场景比较简单,如果有两个 class 类,都在 xml 文件中配置,但是如果 classA 是 classB 的属性,classB 同时也是 classA 的属性,那么该怎么处理呢?究竟先注入哪一个呢?
- 循环依赖的后果:
- 比如,在为 classA 进行 register 操作时,会创建 classA 的 bean,同时将其 PropertyValues 插入,插入时会对其属性(即 classB)进行bean 的注入。
- 然而,对 classB 进行注入又需要用到 class A 的 bean 的注入。
- (估计是因为 tiny 代码不规范的缘故,所以并没有对 classA 的属性进行 bean 的注入,不然一定会报出
org.springframework.beans.factory.BeanCurrentlyInCreationException
的错误。)
- 扩展阅读:从 spring 源码角度分析循环依赖 bean 的组装原理
5.1 互相依赖示例: xml 文件
1 | <bean name="outputService" class="us.codecraft.tinyioc.OutputService"> |
5.2 step 5 的处理办法是:
- 在
beanDefinition
刚被 new 出来时,此时四项都没有被加入。遍历子节点时,如果是 name-value,则将PropertyValues
加入beanDefinition
。 - 如果是 name-ref,则 new 一个
BeanReference
,将 ref 对应的字符串保留在beanReference
的属性区,并把这个beanReference
当作PropertyValue
存入。- 相当于,读取 xml 文件时,将引用关系初始化(记录下来),但不注入。
1 |
|
- 在后续遍历
Map<String,BeanDefinition>
(此处 key 为class 的 name)时,如果遍历到bean == null
时,再进行 newInstance,得到最终的 bean。 - 需要注意,因为对属性增加了
beanReference
,所以在注入 bean 时,会对属性增加 if 判断,然后实例化属性的 bean。- 情景分析:
- 如果先对 classA 进行 setBean,classA 的原始 bean 已经生成,然后对属性(即 classB)进行 setBean。
- classB 会生成原始 bean,并对他的属性(即 classA)进行 setBean,此时可以拿到 classA 的 bean(看似不完整,但有 bean 就可以走完 classB 的注入流程),classB 的 bean 至此完成注入。
- 回到 classA,完成其 bean 的注入。
1 | # 仅展示部分代码: |
step-6-invite-application-context
引入applicationContext
,封装读取xml配置文件,完成bean类的初始化的工作,功能完全一致!
1 | # step-5: 繁琐 |
至此 tiny-spring 的 IOC 部分学习完毕。撒花 ✿✿ヽ(°▽°)ノ✿