Spring——IOC与容器的创建过程
什么是IOC和容器
原本在代码中对象由开发者自己管理,自己去创建。那么把对象交给Sprin去管理,从对象的创建到销毁,都由spring来控制,用户只需要使用的思想,就成为IOC依赖反转。那么spring既然要管理这么多的bean对象,就需要有一个去装载这些对象的地方或者说是东西,在程序运行过程中,从这里面里去拿,那么装载这些bean对象的就叫是容器。
Spring如何解析读取bean对象
要想让Spring管理bean对象,就需要让Spring知道有哪些bean对象需要被管理。常用的方式有很多种,比如xml文件中通过标签的配置方式,properties配置文件,yaml配置文件,注解的等等不通的方式。并且每个类的类名,属性,方法等等信息都是不一样的。那么这里就有两个问题。
- bean的定义方式有很多种,那么解析他的方式对应的也会有很多个不同类,去分别实现不同的解析逻辑。那么在这些类的上面,就会有一个接口,里面定义了解析bean的方法,然后他的子类分别去实现不通格式bean的解析。这个接口就是BeanDefinitionReader
- 在面对不同的bean时,都要交给Spring管理,那么肯定需要解析到一个统一的类对象中去,这个类在Spring源码中就叫做BeanDefinition,它里面负责存放每个被解析的bean对象的名称,属性等等信息。
BeanDefinition放在哪里
都说BeanDefinition放在容器里,这个说法摸棱两可。在非web环境下,我们通常会用ApplicationContext.getBean(“xxx”)去从容器中获取bean对象,这个ApplicationContext叫做上下文对象,虽然他能访问容器了,但他还不是最顶层的东西,在他的上面还有一个BeanFactory,在源码中给的定义是“The root interface for accessing a Spring container”,也就是访问容器的根入口,那么我们操作BeanFactory也就是操作容器,所以在某种意义上说,BeanFactory也就等同于是容器。那么我们通过BeanDefinitionReader解析后得到的BeanDefinition对象都会放在BeanFactory中。
BeanFactoryPostProcessor是干嘛的
有了BeanDefiniction对象之后,里面虽然存放好了被解析的类中的详细信息,但是有了BeanDefiniction对象之后就直接new出来实例化了,这也太简单了,并且这也失去了Spring框架的意义,并且也不适用于很多业务场景。所以,创建bean对象的过程远远不止这样…
在以前用xml文件加载数据源bean的时候,里面的value值往往又是从另一个文件中去读取的,在xml文件中是以${xxxx}的形式表示,那么在BeanDefinitionReader读取装载到BeanDefinition中后,并不会进行值的替换,这个bean中的各个属性的值存放的就是"${xxxx}"的一个字符串,这显然不能达到实例化bean的要求。
<bean id = "dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"></property>
<property name="uername" value="${jdbc.username}"></property>
...
</bean>
所以在这里Spring就有个东西叫做PostProcessor后置处理器的东西。在源码中它有两个分支,一个是BeanPostProcessor 针对Bean对象进行处理,另一个是BeanFactoryPostProcessor 针对BeanFactory进行处理。那么现在BeanDefinition还没有被实例化成Bean,并且放在BeanFactory中,所以这里Spring又会用一个或多个BeanFactoryPostProcessor的实现类做一系列的事情了。比如用PlaceHolderConfigurerSupport实现类去处理${}占位符的值替换。
实例化与初始化的区别
现在BeanDefiniction已经经过了一系列BeanFactoryPostProcessor处理,里面真真实实的记录了一个类的名称,属性,属性值,构造器等等信息。那么现在具备了条件,Spring就直接能把这个bean对象创建出来使用了吗?当然没有那么简单。
对象的创建分为实例化和初始化,实例化只是在堆空间中开辟一块空间给该对象,但此时对象中的属性都是默认值。
实例化后,才开始初始化对象,在初始化中才给对象的属性填充值,并且调用对象的初始化方法等等扩展的功能,完善该对象。
所以,在前面的BeanDefinition经过一系列的BeanFactoryPostProcessor处理之后,紧接着下一步先是通过反射进行实例化。
Spring初始化bean的具体过程
前面提到过,有个东西叫PostProcessor后置处理器的东西。它有两个分支,一个是BeanPostProcessor针对Bean对象进行处理,另一个是BeanFactoryPostProcessor针对BeanFactory进行处理。
那么在这里,初始化的过程中,需要对Bean对象做的一系列事情,肯定就需要BeanFactory来处理了。
在源码中接口上的注释里它已经给我们列举了大致的步骤,如下:
- 填充属性,也就是调用set方法
- 调用aware接口方法
- 执行postProcessBeforeInitialization方法(简称before方法)
- 调用init方法
- 执行postProcessAfterInitialization方法(简称after方法)
- 得到完整的bean对象,可以直接使用
- 销毁
上面主要看BeanPostProcessor接口中的before和after方法。只要在Spring中涉及了PostProcessor的都是可用于扩展操作的。例如AOP动态代理,就是有个AbstractAutoProxyCreatpr类实现了这个接口,在源码里可以看到,它的before方法仅是直接返回对象,没做啥处理,但在after方法中做了一些列操作,先判断bean对象是否为空,如果不为空再判断他是否被代理了等等操作,最后获取这个对象的代理。
总结
Spring中一个对象从解析到加载,再经过层层处理最终变成一个容器中的对象供Spring调用的过程,大致可以总结为以下几步:
- 1.创建容器
- 2.用BeanDefinition读取解析xml配置文件中或yaml各种途径定义的bean信息,封装成BeanDefinition放入容器中
- 3.调用执行BeanFactoryPostProcessor各个实现子类处理- 项目
- 调用BeanFactoryPostProcessor各个实现子类是不是单纯的链式,而是循环执行的
- 4.实例化
- 5.初始化
- 初始化中的填充属性,调用aware,before,init,after各个步骤也不是链式执行的,同样是是循环执行的
- 6.得到完整对象