以下举例皆针对单例模式讨论图解参考:https://www.processon.com/view/link/60e3b0ae0e3e74200e2478ce
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
以 com.gyh.general 包下的 OneBean 为例,debug springboot 启动过程,分析 spring 是如何创建 bean 的。
参考图中 spring 创建 bean 的过程。其中最关键的几步有:1.getSingleton(beanName, true) 依次从一二三级缓存中查找 bean 对象,如果缓存中存在对象,则直接返回 (early);2. createBeanInstance(beanName, mbd, args) 选一个合适的构造函数,new 实例对象 (instance),此时的 instance 中依赖的属性还都是 null,属于半成品;3. singletonFactories.put(beanName, oneSingletonFactory) 利用上一步的 instance,构建一个 singletonFactory,并将其放到三级缓存中;4. populateBean(beanName, mbd, instanceWrapper) 填充 bean:为该 bean 定义的属性创建对象或赋值;5. initializeBean("one",oneInstance, mbd) 初始化 bean:对 bean 进行初始化或其他加工,如生成代理对象 (proxy);6. getSingleton(beanName, false) 依次在一二级缓存中查找,检查是否有因循环依赖导致提前生成的对象,有的话与初始化后的对象是否一致;org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a.one': Bean with name 'a.one' has been injected into other beans [a.two] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.并通过 debug 代码,发现报错位置在 AbstractAutowireCapableBeanFactory#doCreateBean 方法内,由于 earlySingletonReference != null 且 exposedObject != bean,导致报错。
结合流程图中 spring 解决循环依赖 及上述图片中可知:1. 行 1 中 bean 为 createBeanInstance 创建的实例 (address1)2. 行 2 中 exposedObject 为 initializeBean 后生成的代理对象 (address2)3. 行 3 中 earlySingletonReference 为 getEarlyBeanReference 时创建的对象【此处地址同 bean (address1)】深层原因为:先前 TwoBean 在 populateBean 时已经依赖了地址为 address1 的 earlySingletonReference 对象,而此时 OneBean 经过 initializeBean 之后,返回了地址为 address2 的新对象,导致 spring 不知道哪个才是最终版的 bean,所以报错。earlySingletonReference 是如何生成的,参考 getSingleton ("one", true) 过程。
深层原因为:OneBean 先被创建,TwoBean 后创建,再整条链路中,并未在三级缓存中查找过 TwoBean 的 objectFactory 。(OneBean 在创建过程中,被找过两次,即 one-> two ->one;TwoBean 的创建过程中,只找过它一次,即 two ->one。)由此可得:@Async 造成循环依赖报错的先约条件为:1. 循环依赖中的 Bean 使用了 @Async 注解2. 且这个 Bean,比循环内其他 Bean 先创建。3. 注:一个 Bean 可能会同时存在于多个循环内;只要存在它是某个循环内第一个被创建的 Bean,那么就会报错。
3.1.4 为什么同样是代理会产生两种不同的现象?同样是生成代理对象,同样是参与到循环依赖中,会产生不同现象的原因是:当他们处在循环依赖中时,生成代理的节点不同:1. @Transactional 在 getEarlyBeanReference 时生成代理,提前暴露出代理之后的地址(即最终地址);2. @Async 在 initializeBean 时生成代理,导致提前暴露出去的地址不是最终地址,造成报错。为什么 @Async 不能在 getEarlyBeanReference 时生成代理呢?对比下两者执行的代码过程发现:两者都是在 AbstractAutoProxyCreator#getEarlyBeanReference 的方法对原始实例对象进行包装,如下图
使用 @Transactional 的 Bean 在 create proxy 时,获取到一个 advice ,随即生成了代理对象 proxy.
而使用 @Async 的 Bean 在 create proxy 时,没有获取到 advice,不能被代理.
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'c.one': Requested bean is currently in creation: Is there an unresolvable circular reference?debug 代码可知,两个 bean 在根据构造函数 new instance 时,就已经陷入的死循环,无法提前暴露可用的地址,所以只能报错。审核编辑 :李倩
全部0条评论
快来发表一下你的评论吧 !