Spring使用三级缓存解决循环依赖?终于完全弄明白了

您所在的位置:网站首页 缓存主要解决什么问题 Spring使用三级缓存解决循环依赖?终于完全弄明白了

Spring使用三级缓存解决循环依赖?终于完全弄明白了

2024-07-07 15:23| 来源: 网络整理| 查看: 265

文章阅读前推荐

推荐先去看看源码,源码很短,但是对于我们在脑子里构建一个完整思路很重要。看起来非常简单,只需要双击shift,全局查找文件:AbstractAutowireCapableBeanFactory,找到550行左右的doCreateBean方法,重点看一下580行到600行这20行代码就行,包含了三级缓存、属性注入、初始化,精华都在这20行,实在没条件的可以直接看文末附带的doCreateBean方法源码

Spring可以自动解决的循环依赖 public class AService { @Autowired private BService bService; } public class BService { @Autowired private AService aService; } Spring无法自动解决构造器的循环依赖 public class DService { public DService(CService cService) { ... } } public class CService { public CService(DService dService) { ... } } 源码中关于三级缓存的定义 // 一级缓存 private final Map singletonObjects = new ConcurrentHashMap(256); // 二级缓存 private final Map earlySingletonObjects = new HashMap(16); // 三级缓存 private final Map singletonFactories = new HashMap(16);

所以说,其实三级缓存就是三个Map而已

三级缓存有什么区别? 一级缓存

一级缓存里存的是成品对象,实例化和初始化都完成了,我们的应用中使用的对象就是一级缓存中的

二级缓存

二级缓存中存的是半成品,没有完成属性注入和初始化,用来解决对象创建过程中的循环依赖问题 早期暴露出去的Bean,其实也就是解决循环依赖的Bean。早期的意思就是没有完完全全创建好,但是由于有循环依赖,就需要把这种Bean提前暴露出去。其实 早期暴露出去的Bean 跟 完完全全创建好的Bean 他们是同一个对象,只不过早期Bean里面的注解可能还没处理,完完全全的Bean已经处理了完了,但是他们指的还是同一个对象,只不过它们是在Bean创建过程中处于的不同状态

三级缓存

三级缓存中存的是 ObjectFactory 类型的代理工厂对象,用于处理存在 AOP 时的循环依赖问题 存的是每个Bean对应的ObjectFactory对象,通过调用这个对象的getObject方法,就可以获取到早期暴露出去的Bean。 注意:这里有个很重要的细节就是三级缓存只会对单例的Bean生效,像多例的是无法利用到三级缓存的,通过三级缓存所在的类名DefaultSingletonBeanRegistry就可以看出,仅仅是对SingletonBean也就是单例Bean有效果。

发生循环依赖时的执行流程(精华必读)

正常不存在循环依赖的A、B对象是依次创建的,但是如果存在循环依赖的话,创建A的过程中,会顺便把B也创建了。注意,每次获取bean对象都会先去一级缓存看有没有值。 具体流程是: 1、遍历待创建的所有beanName,第一次遍历,开始获取A,此时缓存中没有,会开始正常创建流程 2、A初始创建完成,然后判断A是否是单例,且没有创建完毕,如果是,那么就会把A的beanFactory存入三级缓存 3、A开始处理@Autowired注解,开始注入B属性,于是尝试从缓存获取B,获取不到,则开始正常创建B的流程 4、B初始创建完成,同样判断B是否是单例,且没有创建完毕,如果是,那么就会把B的beanFactory存入三级缓存 5、B开始处理@Autowired注解,开始注入A属性,于是依次从一级缓存、二级缓存查找A属性,都没有就尝试从三级缓存获取A的beanFactory,通过beanFactory.getObject()方法获取A属性,接下来把A存入二级缓存,清除三级缓存。因为此时能获取到A,所以B的A属性能填充成功,B接着执行初始化,B处于实例化、初始化都完成的完全状态 6、B执行addSington(),把完全状态的B存入一级缓存,清空二三级缓存(实际只有三级有值) 7、A继续开始填充B属性,于是调用beanFactory.getBean()获取B,第六步已经把B存入一级缓存,此时直接返回,填充成功,继续执行初始化,得到一个完全状态的A 8、A执行addSington(),把完全状态的A存入一级缓存,清空二三级缓存(实际只有二级有值) 9、第二次遍历,开始获取B,此时一级缓存中有B,直接返回。 至此A、B全部实例化、初始化完成

疑惑解答

问题一:步骤5中,为什么要把B放入二级缓存? 答:主要是怕还有其他的循环依赖,如果还有的话,直接从二级缓存中就能拿到早期的AService对象

问题二:步骤6中,为什么要清空二三级缓存? 答:因为后续其他bean中也需要注入B时,会按顺序从一级缓存直到三级缓存查找,一级缓存有了,二三级缓存中的就不需要了,节省空间

问题三:文章开头提到,构造器造成的循环依赖三级缓存解决不了,为什么? 答:因为构造器循环依赖是发生在bean实例化阶段,此时连早期对象都还没创建出来,拿什么放到三级缓存。三级缓存只能是在bean实例化之后,才能起到作用

问题四:不用三级缓存,只用二级缓存能不能解决循环依赖? 答:不能,因为通过ObjectFactory获取的Bean可能是两种类型,第一种就是实例化阶段创建出来的对象,还是一种就是实例化阶段创建出来的对象的代理对象。至于是不是代理对象,取决于你的配置,如果添加了事务注解又或是自定义AOP切面,那就需要代理。假设舍弃第三级缓存,也就是没有ObjectFactory,那么就需要往第二缓存放入早期的Bean,那么是放没有代理的Bean还是被代理的Bean呢,这是在后面的属性注入阶段,处理注解的时候才能分辨的? 1)如果直接往二级缓存添加没有被代理的Bean,那么可能注入给其它对象的Bean跟最后最后完全生成的Bean是不一样的,因为最后生成的是代理对象,这肯定是不允许的; 2)那么如果直接往二级缓存添加一个代理Bean呢? ● 假设没有循环依赖,提前暴露了代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错, ● 假设没有循环依赖,使用了ObjectFactory,那么就不会提前暴露了代理对象,到最后生成的对象是什么就是什么,就不会报错, ● 如果有循环依赖,不论怎样都会提前暴露代理对象,那么如果跟最后创建好的不一样,那么项目启动就会报错 通过上面分析,如果没有循环依赖,使用ObjectFactory,就减少了提前暴露代理对象的可能性,从而减少报错的可能。

问题五:如果把二级缓存去掉,只留下一级、三级缓存呢? 答:假设舍弃第二级缓存,也就是没有存放早期的Bean的缓存,其实肯定也不行。上面说过,ObjectFactory其实获取的对象可能是代理的对象,那么如果每次都通过ObjectFactory获取代理对象,那么每次都重新创建一个代理对象,这肯定也是不允许的。

doCreateBean()方法源码(带注释) protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } //1、通过BeanDefinition实例化对象 if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance(); Class beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. //对象是否单例、是否未创建完成 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } //将对象的工厂加入到三级缓存 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { //属性注入(在这里解析@Autowired注解时,触发循环依赖) populateBean(beanName, mbd, instanceWrapper); //初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set actualDependentBeans = new LinkedHashSet(dependentBeans.length); 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."); } } } } // Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }

从缓存中获取Bean的源码 protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 从一级缓存中获取 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // 如果一级缓存里没有 且 bean正在创建中 synchronized (this.singletonObjects) { // 从二级缓存里获取 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 二级缓存没有 从三级缓存获取一个工厂 ObjectFactory singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 能获取到工厂 则创建bean singletonObject = singletonFactory.getObject(); // 把实例存入二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); // 把工厂从三级缓存移除 this.singletonFactories.remove(beanName); } } } } return singletonObject; }

–我是“三七有脾气”,一个在互联网"苟且偷生"的Java程序员 “如果感觉博客对你有用,麻烦给个点赞、评论、收藏,谢谢”



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3