Spring源码学习——tiny-spring笔记之IOC

Spring源码学习——tiny-spring笔记之IOC

step1 & step2比较简单,略过,从step3开始记录

Step3

此步骤开始,为了简化BeanDefinition的配置,引入了XML解析器XmlBeanDefinitionReaderXmlBeanDefinitionReader持有一个Map<String,BeanDefinition> registry,使用resourceLoaderResourceResourcespring内部定位资源的接口,这里实际对应的是urlResource)解析到registry中。这里解析XML采用的是org.w3c.dom库,具体可以参考XmlBeanDefinitionReaderdoLoadBeanDefinitions方法。

工厂类AutowireCapableBeanFactory同样持有一个Map<String, BeanDefinition> beanDefinitionMap。通过读取XmlBeanDefinitionReaderMap<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) 方法来 获取一个对象
AbstractBeanFactoryBeanFactory 的一种抽象类实现,规范了 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 结合在了一起。
AbstractApplicationContextApplicationContext 的抽象实现,内部包含一个 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:

  1. https://github.com/code4craft/tiny-spring/blob/master/changelog.md
  2. https://www.zybuluo.com/dugu9sword/note/382745#resource
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值