Spring核心:IOC容器的实现(四)

IOC容器的初始化过程:

Spring的IoC容器初始化包括:Bean定义资源文件的定位、载入和注册3个基本过程。

在上篇讲过,IOC容器的启动是调用了refresh()方法,所以,在这里,我们来看看refresh()的流程:

我们知道,在IOC容器内部的数据结构是BeanDefinition--------->所以--->我们要把XML文件中定义的Bean---------->转换成----------->BeanDefinition

基于这个要求,我们可以把refresh()这个启动方法大致分为三个基本过程:

1、找资源(Resource定位过程)--->BeanDefinition的资源定位---->ReourceLoader通过统一的Resource接口来完成

2、载入资源(BeanDefinition的载入)--->把用户定义好的Bean表示成IoC容器内部的数据结构(BeanDefiniiton)

3、注册资源(IoC容器注册这些BeanDefinition)---------->(IOC容器注册这些BeanDefinition)

注意:IOC容器初始化过程中不包含Bean依赖注入的实现。依赖注入一般发生在应用第一次通过getBean向容器索取Bean的时候。

例外:如果我们对某个Bean设置了lazyinit属性,那么这个Bean的依赖注入在IoC容器初始化时就预先完成了,而不需要等到整个初始化完成以后,第一次使用getBean时才会触发。


BeanDefinition的Resource定位:

1、首先定义一个Resource来定位容器使用的BeanDefinition。

ClassPathResource res = new ClassPathResource("bean.xml");--------------------(用ClassPathReource意味着会在类路径中去寻找)

2、这里定义的Resource还不能由DefaultListableBeanFactory直接使用,还要经过BeanDefinitionReader进行处理

3、经过处理的Resource资源才能由DefaultListableBeanFactory使用。

所以---------在ApplicationContext中--------提供了一系列加载不同Resource的读取器的实现

例:FileSystemXMLApplicationContext、ClassPathXMLApplicationContext、XMLWebApplicationContext等。

一个是从文件系统载入Resource,一个是从类路径中载入Resource,还有一个可以在Web容器中载入。

例:FileSystemXMLApplicationContext对Resource的定位:


Spring核心:IOC容器的实现(四)_第1张图片

从图中可以看出-------FileSystemXmlApplicationContext---继承------AbstractXmlApplicationContext

而进入源码去看,AbstractXmlApplicationContext具备了ResourceLoader读入以Resource定义的BeanDefinition的能力。

因为-----AbstractXmlApplicationContext----继承---------AbstractApplicationContext

而AbstractApplicationContext----------基类------------DefaultResourceLoader

FileSystemXmlApplicationContext的实现:


Spring核心:IOC容器的实现(四)_第2张图片
Spring核心:IOC容器的实现(四)_第3张图片

1、解释:什么是双亲IoC容器?---------子容器可以引用父容器的Bean,但是父容器不能引用子容器的Bean,并且各个子容器中定义的bean是互不可见的,这样也可以避免因为不同的插件定义了相同的bean而带来的麻烦。

应用场景:插件或组件的接入,只需要对方提供JAR即可,由父容器进行引导,各个子容器再完成自己的应该完成的工作即可。

2、通过重载的构造函数,实现了对configuration进行处理的功能,让所有配置在文件系统中的,以XML文件方式存在的BeanDefinition都能够得到有效的处理。

既可以使用一个字符串来配置多个Spring Bean定义资源文件,也可以使用字符串数组,即下面两种方式都是可以的:

a.    ClasspathResource res = new ClasspathResource(“a.xml,b.xml,……”);

多个资源文件路径之间可以是用” ,; /t/n”等分隔。

b.    ClasspathResource res = new ClasspathResource(newString[]{“a.xml”,”b.xml”,……});

因为到最后都是通过同一个构造方法去处理资源文件。

3、super(parent);----------->调用父类容器的构造方法(super(parent)方法)为容器设置好Bean资源加载器:

我们进入到最后调用父类的构造方法是AbstractApplicationContext:


Spring核心:IOC容器的实现(四)_第4张图片
AbstractApplicationContext的构造方法

this();---------->调用了自身的构造函数:

setParent();---------->设置父容器,如果,父容器不为空,并且根据当前环境得到相应数据(即激活不同的Profile,可以得到不同的属性数据,比如用于多环境场景的配置(正式机、测试机、开发机DataSource配置)),存放在Environment对象中。


Spring核心:IOC容器的实现(四)_第5张图片
设置父容器,而这个父容器就是ApplicationContext

而在自身的构造函数中获取一个Spring Source的加载器用于读入Spring Bean定义资源文件:

通过调用内部用protect关键字修饰的方法getResourcePatternResolver获取Spring资源加载器
在这里需要说明一下,因为AbstractApplicationContext继承DefaultResourceLoader,所以也是一个Spring资源加载器,它的getResource(String location)方法也可以用来载入资源。


4、设置完父容器,得到Spring的资源加载器之后,需要通过setConfigLocations():设置Bean定义的资源文件,完成IoC容器Bean定义资源的定位:


Spring核心:IOC容器的实现(四)_第6张图片
解析Bean定义资源文件的路径,处理多个资源文件字符串数组

this.configLocations[i] = resolvePath(locations[i]).trim();------------->resolvePath为同一个类中将字符串解析为路径的方法

先将资源文件的路径都存放到AbstractRefreshableConfigApplicationContext的configLocations变量中,等在加载BeanDefinitions的时候调用getResourceByPath取出来。

而相应的过程就是在refresh()---->obtainFreshBeanFactory()--->refreshBeanFactory()----->loadBeanDefinitions()----->getResources()

5、getResourceByPath():这个方法是一个模板方法,在父类中定义,为读取Resource服务的,这里覆盖父类DefaultResourceLoader的方法,通过Bean定义文件路径封装得到IoC容器要读入的定位Bean定义的资源

getResourceByPath大致调用流程:


Spring核心:IOC容器的实现(四)_第7张图片

refreshBeanFactory:


Spring核心:IOC容器的实现(四)_第8张图片

createBeanFactory():首先通过createBeanFactory构建了一个IoC容器供ApplicationContext使用,这个IoC容器就是DefaultListableBeanFactory。

customizeBeanFactory():对IoC容器进行定制化,如设置启动参数,开启注解的自动装配等

loadBeanDefinitions():调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器。

loadBeanDefinitions:

Spring核心:IOC容器的实现(四)_第9张图片
Spring核心:IOC容器的实现(四)_第10张图片

getReource:


Spring核心:IOC容器的实现(四)_第11张图片

在getResource方法中,最后返回的是调用了getResourceByPath方法,说明这个方法到最后是把获取资源的任务交给getResourceByPath。

getResourceByPath:


Spring核心:IOC容器的实现(四)_第12张图片

前面我们看到的getResourceByPath方法会被FileSystemXmlApplicationContext实现,这个方法返回的是一个FileSystemResource对象,通过这个对象,Spring可以进行相关的I/O操作,完成BeanDefinition的定位。

注意:如果是其他的ApplicationContext,那么会对应生成其他种类的Resource,例:ClassPathResource、ServletContextResource等。

重点:关于IoC容器功能相关的实现,这里是没有涉及的,因为它继承了AbstractXmlApplicationContext,关于IoC容器功能相关的实现,都是在这个类中实现的,在构造函数中通过refresh()来直接启动容器,这样用户可以直接通过实例化FileSystemXmlApplicationContext来启动容器。

总结:在启动过程中,调用了refreshBeanFactory方法,启动并定位Resource资源,到最后把执行定位Resource任务委托给了FileSystemXmlApplicationContext中的getResourceByPath方法。

你可能感兴趣的:(Spring核心:IOC容器的实现(四))