有两个Bean对象,A对象中有一个属性的B对象,B对象中有一个属性的A对象,他们都需要依赖注入,但是map单例池中都没有这个对象。正常情况下在进行注入时,如果map单例池中没有需要的Bean对象B就会去创建这个Bean对象B,但如果需要创建的这个Bean对象B又需要依赖注入对象A,这就造成了循环依赖问题。

Spring使用了三级缓存来解决循环依赖。
现在有两个Bean,进行循环依赖
@Component
public class AService{
@Autowired
BService bService;
public void test(){
System.out.println(bService);
}
}
@Component
public class BService{
@Autowired
AService aService;
}
现在创建Bean的过程如下:
AService 创建的生命周期
- 推断构造函数,创建AService的普通对象
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 找不到就创建AService
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
这就造成了循环依赖
可以引入一个map来解决循环依赖问题,在创建AService普通对象的时候,将这个普通对象存入map中,然后在BService创建的生命周期中进行依赖注入时,如果在单例池中没有找到AService对象就去map中找。
AService 创建的生命周期
- 推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 如果有其他属性就填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 去hushangMap中找,能够找到AService的普通对象,就不用去创建了
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
我们可以发现,到现在为止就使用了两个map,所以明明Spring使用两级缓存就能解决循环依赖为什么最后还要使用三级缓存嘞?肯定是有一些缺陷的。比如,可能AService会进行AOP操作,会创建AServiceProxy代理对象,然后将代理对象放入单例池中,但是BService进行属性赋值 依赖注入的时候是把AService的普通对象进行赋值,所以这里是有问题的,应该是赋值AServiceProxy代理对象。
解决这个问题的思路就是,将AOP的操作提前,如果AService要进行AOP的话,那就就将代理对象放入hushangMap中,而不是放普通对象
AService 创建的生命周期
- 推断构造函数,创建AService的普通对象 —> AOP —> hushangMap<beanName, 代理对象>
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 如果有其他属性就填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 去hushangMap中找 —> 得到AService代理对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
现在我们需要在第一步就去判断该对象是否要进行AOP操作,我们不可能所有的Bean在第一步的时候就去进行AOP,因为在Spring中整体的设计是在第5步才会去进行AOP,只有在某种特殊的情况下才需要在第一步去进行AOP。这个特殊情况就是当前Bean出现了循环依赖的情况下才需要提前进行AOP。
AService 创建的生命周期
出现了循环依赖的情况下才需要提前进行AOP。
- 推断构造函数,创建AService的普通对象 —> 是否满足循环依赖条件?—> AOP —> hushangMap<beanName, 代理对象>
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 如果有其他属性就填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> 去hushangMap中找 —> 得到AService代理对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
我们能够发现,在AService创建中第一步的时候 不太好判断当前Bean是否产生了循环依赖,在BService创建的第二步中才比较好判断是否产生了循环依赖。我们可以再创建AService的时候,将当前创建的Bean的名字存入一个createSet集合中。在BService创建中依赖出入时,如果在单例池中没有找到需要的bean,那就去createSet集合中判断有没有我们需要的Bean,如果有就表示当前需要的Bean对象它自己正在创建中,这也就是循环依赖
AService 创建的生命周期
creatingSet(“AService”)
推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>
依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
如果有其他属性就填充其他属性
初始化前操作 @PostConstruct
初始化 InitialzingBean接口中的afterPropertiesSet()
初始化后 AOF
放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>提前AOP 生成AServiceProxy代理对象,如果Bean本来就不需要进行AOP操作这里就还是获取的普通对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
现在还是有问题的,假如现在有三个Bean,进行循环依赖
@Component
public class AService{
@Autowired
BService bService;
@Autowired
CService cService;
public void test(){
System.out.println(bService);
}
}
@Component
public class BService{
@Autowired
AService aService;
}
@Component
public class CService{
@Autowired
AService aService;
}
那么现在的创建过程如下:
AService 创建的生命周期
creatingSet(“AService”)
推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>
依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
依赖注入,为cService属性赋值—>去单例池SingletonObjects中找CService —> 如果没有就创建CService的Bean对象
如果有其他属性就填充其他属性
初始化前操作 @PostConstruct
初始化 InitialzingBean接口中的afterPropertiesSet()
初始化后 AOF
放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>提前AOP 生成AServiceProxy代理对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
CService 创建的生命周期
- 推断构造函数,创建CService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>提前AOP 生成AServiceProxy代理对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
可以发现上方创建了两个AServiceProxy代理对象,分别赋值给了BService和CService中的属性,这不符合单例Bean,应该的一个AServiceProxy代理对象放入单例池,并且是这个对象进行依赖注入。
解决这个问题就需要使用第二级缓存 earlySingletonObjects,将创建好的AServiceProxy代理对象放入earlySingletonObjects中,key就是beanName,value就是创建好的代理对象,然后CService就从这里拿。
earlySingletonObjects存放的是还没有经过完整生命周期的单例bean对象。
AService 创建的生命周期
creatingSet(“AService”)
推断构造函数,创建AService的普通对象 —> hushangMap<beanName, 普通对象>
依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
依赖注入,为cService属性赋值—>去单例池SingletonObjects中找CService —> 如果没有就创建CService的Bean对象
如果有其他属性就填充其他属性
初始化前操作 @PostConstruct
初始化 InitialzingBean接口中的afterPropertiesSet()
初始化后 AOF
放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>从earlySingletonObjects中拿—>没有获取到,就提前AOP 生成AServiceProxy代理对象 —> 存入earlySingletonObjects<AService, AServiceProxy>
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
CService 创建的生命周期
- 推断构造函数,创建CService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —>createSet找 —> 出现了循环依赖 —>从earlySingletonObjects中拿,得到AServiceProxy代理对象
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池
这里其实是用了三个map,也就是三级缓存,第一个就是单例池singletonObjects用来存放经历了完整Bean生命周期的单例bean,第二个就是earlySingletonObjects存放还没有经过完整Bean生命周期的单例bean,第三个map的用处 就是在BService的第二步中,对AService进行AOP操作是需要AService的普通对象的
Spring里面还是会用到一个map,我们可以暂时理解为前面说的hushangMap,先推断构造函数创建好AService的普通对象后,存入Map中去,key是bean的名字,value中存创建好的bean的名字、普通对象以及beanDefinition,这里有三个对象,value里面如何存放三个对象嘞?Spring是会去定义一个lambda表达式。
AService 创建的生命周期
- creatingSet(“AService”),把当前beanName存入一个set集合中
- 推断构造函数,创建AService的普通对象 —> singletonFactories<beanName, lambda(beanName,普通对象,beanDefinition)>
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找BService —> 如果没有就创建BService的Bean对象
- 如果有其他属性就填充其他属性
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF,假如第2步出现循环依赖已经进行了AOP操作,这里就不用再进行一遍了,如下图所示
- 如果出现了循环依赖,从二级缓存earlySingletonObjects中取代理对象/普通对象
- 放入单例池
BService 创建的生命周期
- 推断构造函数,创建BService的普通对象
- 依赖注入,为aService属性赋值—>去单例池中找AService对象 —> createSet找,判断是否出现了循环依赖 —> 出现了循环依赖 —>从earlySingletonObjects中拿—>如果没有获取到 ----> 从三级缓存singletonFactories找,得到lambda表达式,并执行lambda表达式,执行的方法中会去判断这Bean是否需要进行AOP—>AOP 生成AServiceProxy代理对象;如果不要进行AOP操作 lambda表达式就会得到一个普通对象 —> 存入earlySingletonObjects
- 初始化前操作 @PostConstruct
- 初始化 InitialzingBean接口中的afterPropertiesSet()
- 初始化后 AOF
- 放入单例池

总结:
最终的步骤如下:
AService 创建生命周期
- creatintSet(“AService”)
- 推断构造函数,实例化得到普通对象 —> singletonFactories(lombda(beanName,普通对象,beanDefinition))
- 依赖注入,为bService属性赋值—>去单例池SingletonObjects中找—>没找到 —> 创建BService的Bean对象
- 填充其他属性
- 初始化前操作,@PostConstruct
- 初始化,实现InitialozingBean接口,afterPropertiesSet()方法
- 初始化后,AOP操作,判断是否需要
- 将二级缓存earlySingletonObjects中的代理对象/普通对象取出来
- 存入单例池中
BService 创建生命周期
- 推断构造函数,实例化得到普通对象
- 依赖注入,为aService属性赋值 —> 去singletonObjects中找 —> 没找到 —> creatingSet,判断是否循环依赖 —> 二级缓存earlySingletonObjects中找 —> 没找到 —> singletonFactories --> 代理对象/普通对象 —> 存入二级缓存earlySingletonObjects
- 初始化前操作,@PostConstruct
- 初始化,实现InitialozingBean接口,afterPropertiesSet()方法
- 初始化后,AOP操作,判断是否需要
- 将二级缓存earlySingletonObjects中的代理对象/普通对象取出来
- 存入单例池中
我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He
我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代
我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我试过重新启动apache,缓存的页面仍然出现,所以一定有一个文件夹在某个地方。我没有“公共(public)/缓存”,那么我还应该查看哪些其他地方?是否有一个URL标志也可以触发此效果? 最佳答案 您需要触摸一个文件才能清除phusion,例如:touch/webapps/mycook/tmp/restart.txt参见docs 关于ruby-如何在Ubuntu中清除RubyPhusionPassenger的缓存?,我们在StackOverflow上找到一个类似的问题:
尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot
相信很多人在录制视频的时候都会遇到各种各样的问题,比如录制的视频没有声音。屏幕录制为什么没声音?今天小编就和大家分享一下如何录制音画同步视频的具体操作方法。如果你有录制的视频没有声音,你可以试试这个方法。 一、检查是否打开电脑系统声音相信很多小伙伴在录制视频后会发现录制的视频没有声音,屏幕录制为什么没声音?如果当时没有打开音频录制,则录制好的视频是没有声音的。因此,建议在录制前进行检查。屏幕上没有声音,很可能是因为你的电脑系统的声音被禁止了。您只需打开电脑系统的声音,即可录制音频和图画同步视频。操作方法:步骤1:点击电脑屏幕右下侧的“小喇叭”图案,在上方的选项中,选择“声音”。 步骤2:在“声
首先回顾一下拉格朗日定理的内容:函数f(x)是在闭区间[a,b]上连续、开区间(a,b)上可导的函数,那么至少存在一个,使得:通过这个表达式我们可以知道,f(x)是函数的主体,a和b可以看作是主体函数f(x)中所取的两个值。那么可以有, 也就意味着我们可以用来替换 这种替换可以用在求某些多项式差的极限中。方法: 外层函数f(x)是一致的,并且h(x)和g(x)是等价无穷小。此时,利用拉格朗日定理,将原式替换为 ,再进行求解,往往会省去复合函数求极限的很多麻烦。使用要注意:1.要先找到主体函数f(x),即外层函数必须相同。2.f(x)找到后,复合部分是等价无穷小。3.要满足作差的形式。如果是加
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal