Spring解决循环依赖的关键spring
三级缓存的代码以下:缓存
bean中定义了切面、有@Transactional或@Async这类的加强注解,则必须在元素对象的基础上加上增长逻辑,生成代理类异步
**注意只有一级缓存用了ConcurrentHashMap, 第二级、第三级都是使用了普通的hashmap,由于除了一级缓存,查找、新增和删除二三级缓存节点的关键代码使用同步代码块实现
事实上若是只使用ConcurrentHashMap,不使用同步代码块反而会有问题,由于ConcurrentHashMap只能保证对节点单个操做的原子性、可见性,好比想上面先判断没有再新增就无法完成,get方法也不是一个绝对正确的结果。函数
那么AService和BService完成初始化的过程是什么样的呢?post
这样看好像只须要第一级缓存和第三级缓存就能够解决循环依赖的问题了spa
其实使用三级缓存主要是在须要被代理的bean出现循环依赖的时候起做用.net
咱们先思考一下若是AService上定义了切面,这时候三级缓存中的构造Lambda返回的就不是原始对象了,而是被代理后的对象,这时候上面的流程中第4步中调用Lambda返回的就是AService的一个代理对象
假设AService一样依赖CService,而CService也须要依赖AService,这样AService填充进BService后,会接着填充CService, 使得CService也开始进行建立,CService的建立时须要注入AService,一样能从三级缓存中找到工厂,调用工厂一样返回AService的一个代理对象,这样BService和CService建立过程都生成了一个AService的代理对象,并且持有的都不是同一个AService!代理
其实也比较简单,上面的步骤中第4步BService从三级缓存中获取到工厂生产代理对象时,把这个对象放到第二级缓存中,这样CService须要注入AService时会一二三级缓存依次判断,最后能在二级缓存中直接获得代理对象,就不会去三级缓存中获取工厂构建一个了code
其实生成代理对象通常仍是在执行BeanPostProcessor的这一步执行的,在这一步会根据原始对象生成代理对象放到单例池中
只用出现了循环依赖的时候才会调用三级缓存中的工厂提早进行代理,上面的流程中第四步,BService填充AService时发现BSerice不在单例池中时候,就能够评定必定是出现循环依赖了,接着在二级缓存中也找不到,因而开始找三级缓存中的工厂进行提早代理
可是要是以后BeanPostProcessor有生成一个代理对象放到单例池中,不是又出现了两个代理对象了吗?对象
spring是在执行提早代理加强时把原始对象又放到了一个专门用来作判断是否已经进行了aop的map(earlyProxyReferences)里面,lambda加强代码以下:
在后置处理器执行aop的方法,先去这个map里面去找这个对象,要是发现这个map里面有这个bean了就再也不执行aop了
@Aysnc
注解的Bean的建立代理的时机特别特殊:
@EnableAsync开启时会向容器注入AsyncAnnotationBeanPostProcessor,它是一个BeanPostProcessor,实现了postProcessAfterInitialization,具体的实如今其父类AbstractAdvisingBeanPostProcessor中,在设个PostProcessor会生成一个代理,就及完成了提早代理也会生成一个新的代理
一个类有同时有循环依赖提早代理和 @Async生成代理 的状况时,会生成两个不一样的代理对象,一个是异步的代理对象,就会致使代码在AbstractAutowireCapableBeanFactory.doCreateBean方法中直接抛出错误(最后一步会判断二级缓存中的对象和Processor执行后的对象不一致时抛出异常)
解决办法: