您的位置:首页 > 编程语言 > Java开发

Spring 创建Bean的过程及其源码浅析

2018-01-02 10:26 337 查看

Spring 创建Bean的过程及其源码浅析

spring在加载创建bean的过程中使用了好几种开发模式,主要是:单例模式,和工厂模式(据说spring中有70-80个工厂类),还有代理模式.

(这里我使用的springBoot 1.5.9 对应的spring版本是:4.3.12)

一.创建前的准备工作

Spring在创建我们自定义的bean的时候会有一个准备过程:

1.首先他们要加载单例的工厂对象,并且将他们放到缓存中.(singletonFactories)

2.加载单例提前曝光的对象,并且加入缓存中(earlySingletonObjects)

3.加载单例对象,并且将他们放入缓存.(singletonObjects)

这个是所谓的三级缓存.这是spring解决循环依赖的关键.

那什么是循环依赖呢?



spring在加载我们写的带有@Controller,@service等等的类的时候,如果类中依赖其他类这个时候会先加载被依赖的bean,将被依赖的bean放入到cache中,等所有被依赖的bean都被加载完成时,才会加载该bean.

spring如何判断循环依赖

spring框架中如果发现这个循环依赖无法处理是会抛出一个throw new BeanCurrentlyInCreationException()的异常,那么什么情况会让spring抛出这个异常呢,

首先spring加载BeanA时,发现依赖beanB这时,A会被标记为加载中,去加载beanB,这时加载beanB时发现依赖beanC,beanB标记为加载中,这时加载beanC时发现beanC依赖beanA,而beanA的状态是加载中~~~,这时spring表示玩不下去了,抛出异常BeanCurrentlyInCreationException()

spring如何如何避免循环依赖报错

主要是使用刚才说到的三级缓存,加载bean的时会调用如下方法



让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成
4000
了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化.

二.Bean的加载过程

在看spring源码的过程中发现,其实主要的方法都是已do为前缀,其他大多方法是起到辅助的方法.这里加载bean 的方法名是doCreateBean();



这个方法是bean加载的主方法.也是我们今天要说的方法.



1.传入的bean判断是否是单例,如果是则需要删除工厂bean中缓存的bean,判断为空的情况下会根据bean指定的策略(工厂模式,构造器注入等等)重新实例化bean.

2.创建final修饰的bean,如果之前加载操作不为空分别赋值给bean 和 beanType.

3.给传入的mdb赋值类型.

4.加持锁

5.将合并后的Bean定义Post处理器应用到指定的Bean定义,调用它们的方法.



6.是否需要提前曝光,处理循环依赖的方法.

7.这就是之前我们介绍过处理循环依赖的方法,在创建bean时,在这里引用了这个方法,下面这个getObject()方法主要用于获取对指定bean的早期访问的引用,通常用于解析循环引用。



!!!.这里对bean的再一次依赖引用,我们熟知的AOP也是在这里将advice动态织入bean中,没有操作怎直接返回.

8.用bean定义中的属性值填充给定bean包装中的bean实例.如果存在依赖在递归循环加载bean.

9.初始化给定的bean实例,应用工厂回调以及init方法和bean post处理器



10.获取单例依赖,只有在检测到位循环依赖时候,才会为空.

11.如果有的话,返回依赖于指定bean的所有bean的名称。

12.遍历循环添加依赖的bean

13.这里因为之前该bean依赖的bean已经加载了,如果还为空则抛异常 BeanCurrentlyInCreationException()



14.将给定scope的bean添加到该工厂的一次性bean列表中,注册它的一次性bean接口或在工厂关闭时调用的给定销毁方法(如果适用)。只适用于单例。

三.过程总结

尽管这些调用了很多方法,并且在这个方法中使用了很多设计模式,但是该忽略的忽略,直接看主流程.

1.如果是单例则首先要清理缓存.

2.实例化bean,将BeanDefinition转换成BeanWrapper.转换过程很复杂,包含了

(1)如果是工厂方法则使用工厂方法初始化.

(2)一个类有多个构造器函数,每个函数入参不同,所以需要根据参数锁定构造函数初始化.

(3)做过既不是工厂也是不构造器,则使用默认构造器初始化bean.

3.MergerdBeanDefinitionPostProcessor的应用.bean合并的处理,Autowired注册只是通过预解析.

4.依赖处理.

在spring中会有循环依赖的情况.如果A中有B的属性,而B中又含有A的属性是会出现循环依赖,此时如果A和B都是单例,那么在spring中处理方式就是当创建B的时候,
设计自动注入A的步骤时,并不是再次创建A,而是通过放入缓存中的ObjectFactory来创建实例,这样就解决了循环的问题.

5.属性填充.

6.循环依赖检查

之前说的循环依赖.只对单例有效,spring对其他的bean没有好的解决方法,只能抛异常.

7.注册DisposableBean

8.创建完成并返回

可以看到上面的步骤是比较复杂,这其实这是大体的加载过程,实际的加载过程每一步的源码我们并没有实际的进去看,有兴趣的话可以自己打来看一下.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: