副标题[/!--empirenews.page--]
摘要:
若你是一个有经验的程序员,那你在开发中必然碰到过这种现象:事务不生效。或许刚说到这,有的小伙伴就会大惊失色了。 Spring 不是解决了循环依赖问题吗,它是怎么又会发生循环依赖的呢?,接下来就让我们一起揭秘 Spring 循环依赖的最本质原因。
Spring循环依赖流程图
Spring循环依赖发生原因
- 使用了具有代理特性的BeanPostProcessor
- 典型的有 事务注解@Transactional,异步注解@Async等
源码分析揭秘
- protected Object doCreateBean( ... ){
- ...
- boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
- if (earlySingletonExposure) {
- addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
- }
- ...
-
- // populateBean这一句特别的关键,它需要给A的属性赋值,所以此处会去实例化B~~
- // 而B我们从上可以看到它就是个普通的Bean(并不需要创建代理对象),实例化完成之后,继续给他的属性A赋值,而此时它会去拿到A的早期引用
- // 也就在此处在给B的属性a赋值的时候,会执行到上面放进去的Bean A流程中的getEarlyBeanReference()方法 从而拿到A的早期引用~~
- // 执行A的getEarlyBeanReference()方法的时候,会执行自动代理创建器,但是由于A没有标注事务,所以最终不会创建代理,so B合格属性引用会是A的**原始对象**
- // 需要注意的是:@Async的代理对象不是在getEarlyBeanReference()中创建的,是在postProcessAfterInitialization创建的代理
- // 从这我们也可以看出@Async的代理它默认并不支持你去循环引用,因为它并没有把代理对象的早期引用提供出来~~~(注意这点和自动代理创建器的区别~)
-
- // 结论:此处给A的依赖属性字段B赋值为了B的实例(因为B不需要创建代理,所以就是原始对象)
- // 而此处实例B里面依赖的A注入的仍旧为Bean A的普通实例对象(注意 是原始对象非代理对象) 注:此时exposedObject也依旧为原始对象
- populateBean(beanName, mbd, instanceWrapper);
-
- // 标注有@Async的Bean的代理对象在此处会被生成~~~ 参照类:AsyncAnnotationBeanPostProcessor
- // 所以此句执行完成后 exposedObject就会是个代理对象而非原始对象了
- exposedObject = initializeBean(beanName, exposedObject, mbd);
-
- ...
- // 这里是报错的重点~~~
- if (earlySingletonExposure) {
- // 上面说了A被B循环依赖进去了,所以此时A是被放进了二级缓存的,所以此处earlySingletonReference 是A的原始对象的引用
- // (这也就解释了为何我说:如果A没有被循环依赖,是不会报错不会有问题的 因为若没有循环依赖earlySingletonReference =null后面就直接return了)
- Object earlySingletonReference = getSingleton(beanName, false);
- if (earlySingletonReference != null) {
- // 上面分析了exposedObject 是被@Aysnc代理过的对象, 而bean是原始对象 所以此处不相等 走else逻辑
- if (exposedObject == bean) {
- exposedObject = earlySingletonReference;
- }
- // allowRawInjectionDespiteWrapping 标注是否允许此Bean的原始类型被注入到其它Bean里面,即使自己最终会被包装(代理)
- // 默认是false表示不允许,如果改为true表示允许,就不会报错啦。这是我们后面讲的决方案的其中一个方案~~~
- // 另外dependentBeanMap记录着每个Bean它所依赖的Bean的Map~~~~
- else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
- // 我们的Bean A依赖于B,so此处值为["b"]
- String[] dependentBeans = getDependentBeans(beanName);
- Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
-
- // 对所有的依赖进行一一检查~ 比如此处B就会有问题
- // “b”它经过removeSingletonIfCreatedForTypeCheckOnly最终返返回false 因为alreadyCreated里面已经有它了表示B已经完全创建完成了~~~
- // 而b都完成了,所以属性a也赋值完成儿聊 但是B里面引用的a和主流程我这个A竟然不相等,那肯定就有问题(说明不是最终的)~~~
- // so最终会被加入到actualDependentBeans里面去,表示A真正的依赖~~~
- for (String dependentBean : dependentBeans) {
- if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
- actualDependentBeans.add(dependentBean);
- }
- }
-
- // 若存在这种真正的依赖,那就报错了~~~ 则个异常就是上面看到的异常信息
- if (!actualDependentBeans.isEmpty()) {
- throw new BeanCurrentlyInCreationException(beanName,
- "Bean with name '" + beanName + "' has been injected into other beans [" +
- StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
- "] 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 " +
- "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
- }
- }
- }
- }
- ...
- }
问题简化
- 发生循环依赖时候
Object earlySingletonReference = getSingleton(beanName, false); 肯定有值
- 缓存工厂
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); 将给实例对象添加 SmartInstantiationAwareBeanPostProcessor
AbstractAutoProxyCreator 是 SmartInstantiationAwareBeanPostProcessor 的子类,一定记住了,一定记住, SmartInstantiationAwareBeanPostProcessor 的子类很关键!!!!!
exposedObject = initializeBean(beanName, exposedObject, mbd); 进行 BeanPostProcessor 后置处理,注意是 BeanPostProcessor !!!!!
(编辑:温州站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|