热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

spring学习二spring三级缓存和循环依赖

1循环依赖介绍循环依赖是指两个或两个以上bean互相持有对方最终形成闭环。比如A依赖B,B依赖C,C依赖A循环依赖包括构造器依赖和属性依赖2三级缓存

1 循环依赖介绍

循环依赖是指两个或两个以上bean互相持有对方最终形成闭环。比如A依赖B,B依赖C,C依赖A


循环依赖包括构造器依赖和属性依赖


2 三级缓存解决循环依赖


2.1 spring创建Bean步骤

spring创建bean主要有3个步骤
1 createBeanInstance(实例化bean)
2 populateBean(装配bean)
3 initializeBean(初始化bean)
发生循环依赖的时候主要是在第2步


2.2 三级缓存介绍

spring管理的对象默认是单例的,那么肯定有一个地方来缓存这些对象。spring是通过三级缓存来缓存对象的

/** 一级缓存:bean name和bean实例的缓存 */
private final Map singletOnObjects= new ConcurrentHashMap<>(256);/** 三级缓存:对象工厂缓存 */
private final Map> singletOnFactories= new HashMap<>(16);/** 二级缓存:提前早期对象的缓存,还没完成初始化 */
private final Map earlySingletOnObjects= new HashMap<>(16);

实例化bean之后会先走如下代码

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);}}
}

这段代码的作用是把实例化的bean从二级缓存移除,放到三级缓存

获取bean的代码

protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 从一级缓存中获取对象Object singletOnObject= this.singletonObjects.get(beanName);if (singletOnObject== null && isSingletonCurrentlyInCreation(beanName)) {// 如果一级缓存没有并且这个对象正在创建则从二级缓存获取synchronized (this.singletonObjects) {// 从二级缓存获取beansingletOnObject= 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;
}

当bean初始化完全后会放入一级缓存


2.3 三级缓存解决循环依赖

假如有一个场景A引用B,B引用A,三级缓存工作原理如下
1 实例化A对象
2 把还未完全初始化的A暴露到三级缓存singletonFactories
3 装配bean
4 发现依赖B,就get B
5 B还没有被创建,则进行创建
6 B依赖A,所以get A,由于第二步已经把A放到三级缓存,所以顺利地拿到A,完成初始化,把自己放到一级缓存中
7 A顺利地拿到B,完成初始化。
三级缓存只能解决通过属性注入的循环依赖,不能解决通过构造函数注入的循环依赖。因为把bean放入三级缓存的前提是执行构造函数


2.4 三级缓存的必要性

如果只解决循环依赖问题,一级缓存足矣。但是如果仅仅使用一级缓存,那么该缓存里存放的对象既有完全初始化的,又有不完全初始化的。
如果拿到不完全初始化的对象很容易出现NPE
有人说,那好,我再建一个缓存,一级缓存用来存完全初始化的bena,二级缓存用来存不完全初始化的bean,可以了吧。这样是可以的,但是如果
一个对象被做成切面,那么该对象就会生成一个代理对象。这样依赖注入的bean仍是原始的bean,spring会抛异常。
为了解决代理对象注入的问题,加个三级缓存,里面不存具体的bean,里面存一个工厂对象。通过工厂对象,是可以拿到最终形态的代理后的bean
如果想深入了解三级缓存的必要性可以参考这位大佬的文章:spring解决循环依赖为何用三级缓存


推荐阅读
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • Java程序设计第4周学习总结及注释应用的开发笔记
    本文由编程笔记#小编为大家整理,主要介绍了201521123087《Java程序设计》第4周学习总结相关的知识,包括注释的应用和使用类的注释与方法的注释进行注释的方法,并在Eclipse中查看。摘要内容大约为150字,提供了一定的参考价值。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
author-avatar
天堂调度长
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有