Spring源码学习——tiny-spring笔记之IOC
step1 & step2比较简单,略过,从step3开始记录
Step3
此步骤开始,为了简化BeanDefinition的配置,引入了XML解析器XmlBeanDefinitionReader。XmlBeanDefinitionReader持有一个Map<String,BeanDefinition> registry,使用resourceLoader将Resource(Resource是spring内部定位资源的接口,这里实际对应的是urlResource)解析到registry中。这里解析XML采用的是org.w3c.dom库,具体可以参考XmlBeanDefinitionReader的doLoadBeanDefinitions方法。
工厂类AutowireCapableBeanFactory同样持有一个Map<String, BeanDefinition> beanDefinitionMap。通过读取XmlBeanDefinitionReader的Map<String,BeanDefinition> registry,调用doCreateBean方法,利用java反射来创建bean的实例,保存在beanDefinitionMap中。需要注意,doCreateBean还注入了bean的属性。
具体生成bean就简单了,调用beanFactory.getBean(“beanName”)即可。
各个类的关联分析如下:
类名 | 说明 |
---|---|
Resource | 接口,标识一个外部资源。通过 getInputStream() 方法 获取资源的输入流 。 |
UrlResource | 实现 Resource 接口的资源类,通过 URL 获取资源。 |
ResourceLoader | 资源加载类。通过 getResource(String) 方法获取一个 Resouce 对象,是获取 Resouce 的主要途径。 |
类名 | 说明 |
---|---|
BeanDefinition | 该类保存了 Bean 定义。包括 Bean 的 名字 String beanClassName、类型 Class beanClass、属性 PropertyValues propertyValues。根据其 类型 可以生成一个类实例,然后可以把 属性 注入进去。propertyValues 里面包含了一个个 PropertyValue 条目,每个条目都是键值对 String - Object,分别对应要生成实例的属性的名字与类型。在 Spring 的 XML 中的 property 中,键是 key ,值是 value 或者 ref。对于 value 只要直接注入属性就行了,但是 ref 要先进行解析。Object 如果是 BeanReference 类型,则说明其是一个引用,其中保存了引用的名字,需要用先进行解析,转化为对应的实际 Object。 |
BeanDefinitionReader | 解析 BeanDefinition 的接口。通过 loadBeanDefinitions(String) 来从一个地址加载类定义。 |
AbstractBeanDefinitionReader | 实现 BeanDefinitionReader 接口的抽象类(未具体实现 loadBeanDefinitions,而是规范了 BeanDefinitionReader 的基本结构)。内置一个 HashMap rigistry,用于保存 String - beanDefinition 的键值对。内置一个 ResourceLoader resourceLoader,用于保存类加载器。用意在于,使用时,只需要向其 loadBeanDefinitions() 传入一个资源地址,就可以自动调用其类加载器,并把解析到的 BeanDefinition 保存到 registry 中去。 |
XmlBeanDefinitionReader | 具体实现了 loadBeanDefinitions() 方法,从 XML 文件中读取类定义。 |
类名 | 说明 |
---|---|
BeanFactory | 接口,标识一个 IoC 容器。通过 getBean(String) 方法来 获取一个对象 |
AbstractBeanFactory | BeanFactory 的一种抽象类实现,规范了 IoC 容器的基本结构,但是把生成 Bean 的具体实现方式留给子类实现。IoC 容器的结构:AbstractBeanFactory 维护一个 beanDefinitionMap 哈希表用于保存类的定义信息(BeanDefinition)。获取 Bean 时,如果 Bean 已经存在于容器中,则返回之,否则则调用 doCreateBean 方法装配一个 Bean。(所谓存在于容器中,是指容器可以通过 beanDefinitionMap 获取 BeanDefinition 进而通过其 getBean() 方法获取 Bean。) |
AutowireCapableBeanFactory | 可以实现自动装配的 BeanFactory。在这个工厂中,实现了 doCreateBean 方法,该方法分三步:1,通过 BeanDefinition 中保存的类信息实例化一个对象;2,把对象保存在 BeanDefinition 中,以备下次获取;3,为其装配属性。装配属性时,通过 BeanDefinition 中维护的 PropertyValues 集合类,把 String - Value 键值对注入到 Bean 的属性中去。如果 Value 的类型是 BeanReference 则说明其是一个引用(对应于 XML 中的 ref),通过 getBean 对其进行获取,然后注入到属性中。 |
具体调用、创建Bean的过程如下:
@Test
public void test() throws Exception {
// 1.读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
// 2.初始化BeanFactory并注册bean
BeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
}
Step4
为Bean注入Bean。在上一步的基础上,增加了一个类叫做BeanReference,用于保存Bean中的属性Bean。
为了解决循环引用的问题,采用了懒加载(Lazy-Init)。将createBean的事情放到getBean的时候才执行,而工厂在注册Bean的时候并没有真正创建Bean。这样在注入bean的时候,如果该属性对应的bean找不到,那么就先创建。因为总是先创建后注入,所以不会存在两个循环依赖的bean创建死锁的问题。
Step5
添加ApplicationContext
类名 | 说明 |
---|---|
ApplicationContext | 标记接口,继承了 BeanFactory。通常,要实现一个 IoC 容器时,需要先通过 ResourceLoader 获取一个 Resource,其中包括了容器的配置、Bean 的定义信息。接着,使用 BeanDefinitionReader 读取该 Resource 中的 BeanDefinition 信息。最后,把 BeanDefinition 保存在 BeanFactory 中,容器配置完毕可以使用。注意到 BeanFactory 只实现了 Bean 的 装配、获取,并未说明 Bean 的 来源 也就是 BeanDefinition 是如何 加载 的。该接口把 BeanFactory 和 BeanDefinitionReader 结合在了一起。 |
AbstractApplicationContext | ApplicationContext 的抽象实现,内部包含一个 BeanFactory 类。主要方法有 getBean() 和 refresh() 方法。getBean() 直接调用了内置 BeanFactory 的 getBean() 方法,refresh() 则用于实现 BeanFactory 的刷新,也就是告诉 BeanFactory 该使用哪个资源(Resource)加载类定义(BeanDefinition)信息,该方法留给子类实现,用以实现 从不同来源的不同类型的资源加载类定义 的效果。 |
ClassPathXmlApplicationContext | 从类路径加载资源的具体实现类。内部通过 XmlBeanDefinitionReader 解析 UrlResourceLoader 读取到的 Resource,获取 BeanDefinition 信息,然后将其保存到内置的 BeanFactory 中。 |
这里ApplicationContext用到了类似于java io的装饰者设计模式,一方面继承了BeanFactory,另一方面又持有BeanFactory的实例,直接调用该实例的getBean()方法。ApplicationContext加强了BeanFactory,它把类定义的加载也包含进去了。
Reference: