目录

文章目录

前言

一、spring三级缓存是什么?

二、分析步骤

总结


前言

spring三级缓存用来解决spring的循环依赖问题

目前循环依赖只在属性set注入且单例的情况下才能解决

构造器注入和多例的循环依赖情况下是无法解决的


提示:以下是本篇文章正文内容,下面案例可供参考

一、spring三级缓存是什么?

singletonObjects   <ConCurrentHashMap<>>  一级缓存
singletonFactories  <HashMap<>>  三级缓存
earlySingletonObjects  <HashMap<>> 二级缓存

二、分析步骤

1.循环依赖示例:

@Service
public class AService {private BService bService;public AService() {System.out.println("Aservice 构造方法执行---------");}public BService getbService() {return bService;}public void setbService(BService bService) {this.bService = bService;}
}@Service
public class BService {private AService aService;public BService() {System.out.println("bService 构造方法--------");}public AService getaService() {return aService;}public void setaService(AService aService) {this.aService = aService;}
}

当容器初始化加载两个bean时,会造成A,B相互依赖,查看bean生命周期,可以追踪循环依赖的解决方式

首先容器会通过doGetBean()获取AService,

/*** Return the (raw) singleton object registered under the given name.* <p>Checks already instantiated singletons and also allows for an early* reference to a currently created singleton (resolving a circular reference).* @param beanName the name of the bean to look for* @param allowEarlyReference whether early references should be created or not* @return the registered singleton object, or {@code null} if none found*/@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {//初始首先从一级缓存中获取Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;}
// 如果获取不到,则创建
if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {//创建return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}              

通过doCreateBean()方法创建Aservice

// 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));}//放入三级缓存后,准备初始化bean实例,接着填充属性操作
// Initialize the bean instance.Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);exposedObject = initializeBean(beanName, exposedObject, mbd);}

会先把AService放入三级缓存,

key: beanName     value值 : () -> getEarlyBeanReference(beanName, mbd, bean)

具体的addSingletonFactory() 方法

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}

AService填充属性,需要注入BService

解析依赖方法:

private Object resolveReference(Object argName, RuntimeBeanReference ref) {try {Object bean;String refName = ref.getBeanName();refName = String.valueOf(doEvaluate(refName));if (ref.isToParent()) {if (this.beanFactory.getParentBeanFactory() == null) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Can't resolve reference to bean '" + refName +"' in parent factory: no parent factory available");}bean = this.beanFactory.getParentBeanFactory().getBean(refName);}else {//此处会去获取BService bean实例bean = this.beanFactory.getBean(refName);this.beanFactory.registerDependentBean(refName, this.beanName);}if (bean instanceof NullBean) {bean = null;}return bean;}catch (BeansException ex) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);}}

而容器中还没有Bservice的Bean,及需要先创建BService的bean

BService的创建也是先经过上述步骤

先把BService的未成品的bean放入三级缓存

然后BService的bean 填充属性 AService

这时getSingleton()方法如下

protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);//从一级缓存未找到AService的Bean且allowEarlyReference为true,//则把AService的Bean放到二级缓存中if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;}

之后BService的Bean属性填充完毕,创建实例Bean完成,让后将Bean放入的一级缓存

/*** Add the given singleton object to the singleton cache of this factory.* <p>To be called for eager registration of singletons.* @param beanName the name of the bean* @param singletonObject the singleton object*/protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}

接下来回到Aservice的Bean的 填充属性方法

获取到Bservice的Bean实例,

属性填充完毕

再把AService的Bean实例放到一级缓存中,

至此实例化ABService完成


总结

循环依赖似乎只要二级缓存就能解决,为何还要三级缓存,

个人理解:其实在最开始放入三级缓存的value,lambda表达式,即用来解决AOP代理的bean相互依赖的的问题,如果需要代理,则提前暴露代理对象放入的其他缓存,否则提前生成普通代理对象实例放入其他缓存即可

spring三级缓存相关推荐

  1. Spring三级缓存详解

    Spring三级缓存是为了解决对象间的循环依赖问题. A依赖B,B依赖A,这就是一个简单的循环依赖. 我们来先看看三级缓存的源码. (1)查看"获取Bean"的源码,注意getSi ...

  2. Spring三级缓存源码

    文章目录 Spring三级缓存类源码分析 Spring三级缓存类源码分析 public class DefaultSingletonBeanRegistry extends SimpleAliasRe ...

  3. Spring三级缓存解决循环依赖问题详解

    spring三级缓存解决循环依赖问题详解 前言 这段时间阅读了spring IOC部分的源码.在学习过程中,自己有遇到过很多很问题,在上网查阅资料的时候,发现很难找到一份比较全面的解答.现在自己刚学习 ...

  4. 什么是Spring三级缓存 对象在三级缓存中的创建流程 【三级缓存 循环依赖】

    一.什么是Spring三级缓存 第一级缓存:也叫单例池,存放已经经历了完整生命周期的Bean对象. 第二级缓存:存放早期暴露出来的Bean对象,实例化以后,就把对象放到这个Map中.(Bean可能只经 ...

  5. spring三级缓存以及@Async产生循环引用

    spring三级缓存以及@Async产生循环引用 spring三级缓存介绍 三级缓存解除循环引用原理 源码对应 1.获取A,从三级缓存中获取,没有获取到 2.构造A,将A置入三级缓存 构造A(创建A实 ...

  6. spring 三级缓存_通过画图+视频把循环依赖、监听器等等spring源码讲明白了

    大家在阅读源码的时候有没有这种感觉:每次要看源码的时候十分信誓旦旦逼迫自己努力看着源码,但是还没看多长时间就会感觉枯燥,无味没意思,所以我是十分不愿意去看源码,但是今天福利来了,有位大神通过画图+视频 ...

  7. 面试官:连Spring三级缓存都答不好,自己走还是我送你?

    面试官:简历上写了精通Spring,那你回答一下Spring为什么用"三级缓存"去解决循环依赖? 我:.......应该有三个缓存的map结构 面试官:具体回答一下 我:平时没认真 ...

  8. Spring三级缓存解决循环依赖

    1. 前言 循环依赖:就是N个类循环(嵌套)引用. 通俗的讲就是N个Bean互相引用对方,最终形成闭环.用一副经典的图示可以表示成这样(A.B.C都代表对象,虚线代表引用关系): 其实可以N=1,也就 ...

  9. Spring三级缓存以及面试题

    Spring的三级缓存 三级缓存的作用:解决循环依赖的问题 循环依赖问题:说白是一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用 代码描述: @Service pub ...

  10. spring 三级缓存源码分析

    spring中对于循环依赖的解决采用了三级缓存机制,即: Map<String, Object> singletonObjects //第一级缓存,存放初始化完成的bean Map< ...

最新文章

  1. BZOJ.3527.[ZJOI2014]力(FFT)
  2. Blender着色器纹理材质创作教程含源文件 Shader Forge
  3. 5 加盐_洗花甲时,别只放盐了!老渔民教您一招,5分钟就搞定,太省事了
  4. 稳压二级管原理之详解
  5. java function获取参数_「Java容器」ArrayList源码,大厂面试必问
  6. 记录一次Socket编程:OutputStream的flush方法
  7. php mcript(),PHP基于mcript扩展实现对称加密功能
  8. java 多线程下载 开源_Android Downloader是一个开源的多线程,多任务下载框架
  9. Pa interface issue——PA_EXCEED_ROUND_LIMIT
  10. 百度UEditor编辑器使用(二)
  11. C++自学21:动态分配内存(malloc/calloc/realloc/new)/回收内存(free/delete)
  12. 阿里研究院:解读互联网经济十大议题
  13. 客观评价golang的优缺点
  14. Logstash filter 插件之 grok
  15. 计算机设置u盘启动,如何设置U盘启动_BIOS设置U盘启动教程 - U当家官网
  16. 知其然,知其所以然——ArrayList.add()详解
  17. hibernate笔记(三)
  18. excel表格打印每页都有表头_教你一招Excel打印技巧:每页有表头标题,阅读起来是真方便...
  19. 在AID Learning中用IPad或电脑连接手机
  20. Excel 基础的操作

热门文章

  1. sql2008安装图解 sql server 2008 R2安装教程
  2. 小程序图片上传无反应
  3. origin 截断y轴
  4. visio箭头尾部遮盖方框边线
  5. visio中控制箭头
  6. 点阵字模生成原理与方法
  7. Java中swing修改左上角的图标
  8. python pyhook_python使用pyHook.HookManager()返回来的event中,event.Time肿么转换成为datetime形式?...
  9. 修复pdf字体嵌入问题
  10. 算法评测在本地生活地图技术领域的探索和实践