Spring的bean加载的过程

一、源码跟踪

在这里插入图片描述
通过getBean(“instanceA”)做为入口分析bean的加载过程:
debug模式F5进入,展示如下图
在这里插入图片描述
从此处做为入口进入,主要是寻找哪一行代码给我们返回的instanceA实例,很明显此处做了实例的返回,展示入下图
在这里插入图片描述
继续进入跟踪,展示如下图
在这里插入图片描述
调用重载的方法跟踪到最后
在这里插入图片描述
这里就涉及到spring容器的三级缓存,展示如下图:
在这里插入图片描述
这个singletonObjects.get(beanName)的singletonObjects就是我们经常提的容器,展示如下图:
在这里插入图片描述
在这里插入图片描述
这是个ConcurrentHashMap,如果从一个map中get一个Bean元素,那么Spring IOC肯定是已经提前将Bean元素放置到map中过。通过全局搜索发现put一个Bean元素的方法,如下图:
在这里插入图片描述
虽然搜索出此方法调用put,但是无法确定都是经过哪些方法调用后走此方法,此时通过debug的调用栈信息追踪,如下图:
在这里插入图片描述
发现是经过我们的Main方法初始化创建IOC容器时调用了put,推导出当IOC容器启动时,会将所有的单例对象创建好put到单例缓存池singletonObjects中。通过调用栈信息观察最核心的关键方法如下图:
在这里插入图片描述
在这里插入图片描述
追踪分析doGetBean方法,如下图:
在这里插入图片描述
在这里插入图片描述
继续往下走,此时单例对象缓存池(一级缓存)中没有存入过Bean对象,走判断为null的逻辑,然后getParentBeanFactory方法就是获取父容器,此时肯定是不存在父容器。如下图判断为null
在这里插入图片描述
继续跟踪,获取当前Bean定义,然后判断当前Bean定义是否为Abstract,如果是则无法进行实例化,如下图展示:
在这里插入图片描述
继续跟踪,通过Bean定义判断是否为单例,肯定为true,进入如下代码,通过getSingleton(beanName)方法返回一个Bean实例,参数为beanName和一个lambda表达式的匿名函数创建的参数,展示如下图:
在这里插入图片描述
在这里插入图片描述
beforeSingletonCreation(beanName)是用来判断循环依赖的依据,往set集合中添加当前创建的beanName中,如果下一次创建同一个beanName会报异常,起一个标记作用
在这里插入图片描述
进入createBean方法,追踪到如下两个方法返回Bean实例,如下图:
在这里插入图片描述
进入doCreateBean方法进行追踪,通过此方法createBeanInstance()会创建一个空壳对象实例instanceWrapper,展示如下图:

在这里插入图片描述
通过对空壳对象beanWrapper构造早期对象earlyBean(没有对属性赋值的bean),并添加到三级缓存中
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
doCreateBean方法继续往下执行,到populateBean()方法给属性进行赋值(开始注入动作),如下图:
在这里插入图片描述

二、循环依赖问题

循环依赖相关文章

Aservice {
    BService
}

Bservice{
    AService
}

循环依赖代码减构
-------------------------------------------------------------

v1.0
-------------------------------------------------------------
Aservice bean的生命周期

1. 实例化----Aservice对象(new Aservice())
2. 填充Bservice属性----从单例池(一级缓存)中找bservice-------找不到-----------创建bservice(进入bservice生命周期)
   2.1  实例化Bservice对象
   2.2  填充Aservice属性-------从单例池中找Aservice-------找不到(因为单例池中存放的都是完整的对象)------- 创建Aservice对象
			   (此时又进入A对象bean的生命周期,产生循环依赖,那么就产生了一个想法,在这个对象创建生命周期实例化创建对象后,将对象存入到一个map中)
3. 填充其他属性
4. 做其他事情
5. 放入单例池

v2.0
-------------------------------------------------------------

Aservice bean的生命周期

1. 实例化----Aservice对象(new Aservice())-----放入Map<AserviceName,Aservice对象>
2. 填充Bservice属性----从单例池(一级缓存)中找bservice-------找不到-----------创建bservice(进入bservice生命周期)
   2.1  实例化Bservice对象------放入Map<BserviceName,Bservice对象>
   2.2  填充Aservice属性-------从单例池中找Aservice-------找不到(因为单例池中存放的都是完整的对象)---(此处修改为)从Map中获取A对象(此时A为不完整对象,因为A的生命周期还没有走完)
   2.3  填充其他属性
   2.4  做其他事情
   2.5  放入单例池
   -----------------------此时BService对象完成了生命周期,后续AService的生命周期也随之完成
3. 填充其他属性 
4. 做其他事情
5. 放入单例池

v2.0(问题)
-------------------------------------------------------------
如果AService对象使用AOP被切了,那么Aservice对象则在实例化的时候则是一个代理对象
Aservice bean的生命周期

1. 实例化----Aservice对象(new Aservice())-----放入Map<AserviceName,Aservice对象>
2. 填充Bservice属性----从单例池(一级缓存)中找bservice-------找不到-----------创建bservice(进入bservice生命周期)
   2.1  实例化Bservice对象------放入Map<BserviceName,Bservice对象>
   2.2  填充Aservice属性-------从单例池中找Aservice-------找不到(因为单例池中存放的都是完整的对象)---(此处修改为)从Map中获取A对象(此时A为不完整对象,因为A的生命周期还没有走完)(对应第四步的代理对象,此处map中存放的不是A的代理对象,出现问题)
   2.3  填充其他属性
   2.4  做其他事情
   2.5  放入单例池
   -----------------------此时BService对象完成了生命周期,后续AService的生命周期也随之完成
3. 填充其他属性 
4. 做其他事情(正常在生命周期的这个步骤生成A代理对象,并放入单例池,那么填充到B对象的A属性也应该是代理对象)
5. 放入单例池

v3.0(2.0问题解决)
-------------------------------------------------------------

对于2.0问题将实例化对象的代理对象生成提前
---------
如何判断是否提前进行AOP:当Aservice出现循环依赖的情况则进行提前AOP判断,否则正常在第四步进行AOP。
---------
但是在1.过程是无法判断是否出现循环依赖,解决办法:将判断放置到2.2比较容易

使用set集合存储正在创建对象,创建完毕删除对象,从集合中判断对象是否存在


1. 实例化----Aservice对象(new Aservice())------判断是否需要AOP,需要则生成A代理对象-----放入Map<Aservice代理,Aservice代理对象>
2. 填充Bservice属性----从单例池(一级缓存)中找bservice-------找不到-----------创建bservice(进入bservice生命周期)
   2.1  实例化Bservice对象------放入Map<BserviceName,Bservice对象>
   2.2  填充Aservice属性-------从单例池中找Aservice-------找不到(因为单例池中存放的都是完整的对象)---判断A对象正在创建中(循环依赖判断方法)--(提前AOP)从Map中获取A对象(如果为代理对象则获取的是代理对象)
																		(此时A为不完整对象,因为A的生命周期还没有走完)
																	      --  如果Map中不存在,将代理对象放入map.此时使用map存储是为了如果C对象也依赖了A对象,
																	          那么C对象又会创建一个代理对象
																	         ,那么就会出现两个A代理对象,因此使用Map存储,C就可以从map中获取即可。
   2.3  填充其他属性
   2.4  做其他事情
   2.5  放入单例池
   -----------------------此时BService对象完成了生命周期,后续AService的生命周期也随之完成
3. 填充其他属性 
4. 做其他事情(正常在生命周期的这个步骤生成A代理对象,并放入单例池,那么填充到B对象的A属性也应该是代理对象)
   当A对象属性填充完毕后,从二级缓存中取出放入一级缓存(单例池),并删除
5. 放入单例池


单例池:一级缓存 
Map: 二级缓存




v4.0(3.0问题解决)
-------------------------------------------------------------
如果3.0是为了解决AOP的循环依赖问题的逻辑


比如说不支持AOP



比如说没有出现循环依赖

单纯对于AOP生成代理对象本身也依赖被代理对象
那么A对象生成时,依赖B,B生成完成后,生成代理对象返回代理对象。但是单例对象并没有进行属性填充操作,所以A代理对象的B属性为null

如果需要提前进行AOP,那么就需要A对象的被代理对象(原对象),这个原对象就需要存放到三级缓存中获取 




1. 实例化----Aservice对象(new Aservice())-----存入三级缓存(Aname,lambda(A对象,beanname,beandefinition))
2. 填充Bservice属性----从单例池(一级缓存)中找bservice-------找不到---------创建bservice(进入bservice生命周期)
   2.1  实例化Bservice对象------放入Map<BserviceName,Bservice对象>
   2.2  填充Aservice属性-------从单例池中找Aservice-------找不到(因为单例池中存放的都是完整的对象)---判断A对象正在创建中(循环依赖判断方法)--(提前AOP)从Map中获取A对象(如果为代理对象则获取的是代理对象)
																		(此时A为不完整对象,因为A的生命周期还没有走完)
																	      --  如果二级缓存中不存在,则从三级缓存中获取lambda,这个lambda就是用于创建代理对象的AOP
   2.3  填充其他属性
   2.4  做其他事情
   2.5  放入单例池
   -----------------------此时BService对象完成了生命周期,后续AService的生命周期也随之完成
3. 填充其他属性 
4. 做其他事情(正常在生命周期的这个步骤生成A代理对象,并放入单例池,那么填充到B对象的A属性也应该是代理对象)
   当A对象属性填充完毕后,从二级缓存中取出放入一级缓存(单例池),并删除
5. 放入单例池

循环依赖 A中依赖B,B中依赖A,如下图:
在这里插入图片描述

简单的循环依赖(没有AOP)

在这里插入图片描述
在这里插入图片描述

结合了AOP的循环依赖

调用到AnnotationAwareAspectJAutoProxyCreator的getEarlyBeanReference方法,对A进行了AOP代理的话,那么此时getEarlyBeanReference将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。

  • 在给B注入的时候为什么要注入一个代理对象?
    当我们对A进行了AOP代理时,说明我们希望从容器中获取到的就是A代理后的对象而不是A本身,因此把A当作依赖进行注入时也要注入它的代理对象

  • 明明初始化的时候是A对象,那么Spring是在哪里将代理对象放入到容器中的呢?
    在这里插入图片描述
    在完成初始化后,Spring又调用了一次getSingleton方法,这一次传入的参数又不一样了,false可以理解为禁用三级缓存,前面图中已经提到过了,在为B中注入A时已经将三级缓存中的工厂取出,并从工厂中获取到了一个对象放入到了二级缓存中,所以这里的这个getSingleton方法做的时间就是从二级缓存中获取到这个代理后的A对象。

  • 三级缓存为什么要使用工厂而不是直接使用引用?换而言之,为什么需要这个三级缓存,直接通过二级缓存暴露一个引用不行吗?
    这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象,否则只会创建一个工厂并将其放入到三级缓存中,但是不会去通过这个工厂去真正创建对象。即使没有循环依赖,也会将其添加到三级缓存中,而且是不得不添加到三级缓存中,因为到目前为止Spring也不能确定这个Bean有没有跟别的Bean出现循环依赖。假设我们在这里直接使用二级缓存的话,那么意味着所有的Bean在这一步都要完成AOP代理。

加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值