范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Spring的三级缓存该怎么学?

  这是一篇硬核文章观看理解时间需长,真的是硬核,教你理解什么是Spring的三级缓存与怎么解决三级缓存跟三级缓存出现的场景,细节决定成败,加油,打工人它
  1.由同事抛的一个问题开始
  最近项目组的一个同事遇到了一个问题,问我的意见,一下子引起的我的兴趣,因为这个问题我也是第一次遇到。平时自认为对spring循环依赖问题还是比较了解的,直到遇到这个和后面的几个问题后,重新刷新了我的认识。
  我们先看看当时出问题的代码片段:  @Service publicclass TestService1 {      @Autowired     private TestService2 testService2;      @Async     public void test1() {     } } @Service publicclass TestService2 {      @Autowired     private TestService1 testService1;      public void test2() {     } }
  这两段代码中定义了两个Service类: TestService1 和TestService2 ,在TestService1中注入了TestService2的实例,同时在TestService2中注入了TestService1的实例,这里构成了循环依赖 。
  只不过,这不是普通的循环依赖,因为TestService1的test1方法上加了一个 @Async 注解。
  大家猜猜程序启动后运行结果会怎样?  org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name "testService1": Bean with name "testService1" has been injected into other beans [testService2] 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.
  报错了…原因是出现了循环依赖。
  「不科学呀,spring不是号称能解决循环依赖问题吗,怎么还会出现?」
  如果把上面的代码稍微调整一下:  @Service publicclass TestService1 {      @Autowired     private TestService2 testService2;      public void test1() {     } }
  把TestService1的test1方法上的 @Async 注解去掉,TestService1 和TestService2 都需要注入对方的实例,同样构成了循环依赖。
  但是重新启动项目,发现它能够正常运行。这又是为什么?
  带着这两个问题,让我们一起开始spring循环依赖的探秘之旅。  2.什么是循环依赖?
  循环依赖:说白是一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用。
  第一种情况:自己依赖自己的直接依赖
  第二种情况:两个对象之间的直接依赖
  第三种情况:多个对象之间的间接依赖
  前面两种情况的直接循环依赖比较直观,非常好识别,但是第三种间接循环依赖的情况有时候因为业务代码调用层级很深,不容易识别出来。  3.循环依赖的N种场景
  spring中出现循环依赖主要有以下场景:
  单例的setter注入
  这种注入方式应该是spring用的最多的,代码如下:  @Service publicclass TestService1 {      @Autowired     private TestService2 testService2;      public void test1() {     } } @Service publicclass TestService2 {      @Autowired     private TestService1 testService1;      public void test2() {     } }
  这是一个经典的循环依赖,但是它能正常运行,得益于spring的内部机制,让我们根本无法感知它有问题,因为spring默默帮我们解决了。
  spring内部有三级缓存:  singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例  earlySingletonObjects 二级缓存,用于保存实例化完成的bean实例  singletonFactories 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。
  下面用一张图告诉你,spring是如何解决循环依赖的:
  图1
  细心的朋友可能会发现在这种场景中第二级缓存作用不大。
  那么问题来了,为什么要用第二级缓存呢?
  试想一下,如果出现以下这种情况,我们要如何处理?  @Service publicclass TestService1 {      @Autowired     private TestService2 testService2;     @Autowired     private TestService3 testService3;      public void test1() {     } } @Service publicclass TestService2 {      @Autowired     private TestService1 testService1;      public void test2() {     } } @Service publicclass TestService3 {      @Autowired     private TestService1 testService1;      public void test3() {     } }
  TestService1依赖于TestService2和TestService3,而TestService2依赖于TestService1,同时TestService3也依赖于TestService1。
  按照上图的流程可以把TestService1注入到TestService2,并且TestService1的实例是从第三级缓存中获取的。
  假设不用第二级缓存,TestService1注入到TestService3的流程如图:
  图2
  TestService1注入到TestService3又需要从第三级缓存中获取实例,而第三级缓存里保存的并非真正的实例对象,而是 ObjectFactory 对象。说白了,两次从三级缓存中获取都是ObjectFactory 对象,而通过它创建的实例对象每次可能都不一样的。
  这样不是有问题?
  为了解决这个问题,spring引入的第二级缓存。上面图1其实TestService1对象的实例已经被添加到第二级缓存中了,而在TestService1注入到TestService3时,只用从第二级缓存中获取该对象即可。
  还有个问题,第三级缓存中为什么要添加 ObjectFactory 对象,直接保存实例对象不行吗?
  答:不行,因为假如你想对添加到三级缓存中的实例对象进行增强,直接用实例对象是行不通的。
  针对这种场景spring是怎么做的呢?
  答案就在 AbstractAutowireCapableBeanFactory 类doCreateBean 方法的这段代码中:
  多例的setter注入
  这种注入方法偶然会有,特别是在多线程的场景下,具体代码如下:  @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Service publicclass TestService1 {      @Autowired     private TestService2 testService2;      public void test1() {     } } @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @Service publicclass TestService2 {      @Autowired     private TestService1 testService1;      public void test2() {     } }
  很多人说这种情况spring容器启动会报错,其实是不对的,我非常负责任的告诉你程序能够正常启动。
  为什么呢?
  其实在 AbstractApplicationContext 类的refresh 方法中告诉了我们答案,它会调用finishBeanFactoryInitialization 方法,该方法的作用是为了spring容器启动的时候提前初始化一些bean。该方法的内部又调用了preInstantiateSingletons 方法
  标红的地方明显能够看出:非抽象、单例 并且非懒加载的类才能被提前初始bean。
  而多例即 SCOPE_PROTOTYPE 类型的类,非单例,不会被提前初始化bean,所以程序能够正常启动。
  如何让他提前初始化bean呢?
  只需要再定义一个单例的类,在它里面注入TestService1  @Service publicclass TestService3 {      @Autowired     private TestService1 testService1; }
  重新启动程序,执行结果:  Requested bean is currently in creation: Is there an unresolvable circular reference?
  果然出现了循环依赖。
  注意:这种循环依赖问题是无法解决的,因为它没有用缓存,每次都会生成一个新对象。  构造器注入
  这种注入方式现在其实用的已经非常少了,但是我们还是有必要了解一下,看看如下代码:  @Service publicclass TestService1 {      public TestService1(TestService2 testService2) {     } } @Service publicclass TestService2 {      public TestService2(TestService1 testService1) {     } }
  运行结果:  Requested bean is currently in creation: Is there an unresolvable circular reference?
  出现了循环依赖,为什么呢?
  从图中的流程看出构造器注入没能添加到三级缓存,也没有使用缓存,所以也无法解决循环依赖问题。  单例的代理对象setter注入
  这种注入方式其实也比较常用,比如平时使用: @Async 注解的场景,会通过AOP 自动生成代理对象。
  我那位同事的问题也是这种情况。  @Service publicclass TestService1 {      @Autowired     private TestService2 testService2;      @Async     public void test1() {     } } @Service publicclass TestService2 {      @Autowired     private TestService1 testService1;      public void test2() {     } }
  从前面得知程序启动会报错,出现了循环依赖:  org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name "testService1": Bean with name "testService1" has been injected into other beans [testService2] 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.
  为什么会循环依赖呢?
  答案就在下面这张图中:
  说白了,bean初始化完成之后,后面还有一步去检查:第二级缓存 和 原始对象 是否相等。由于它对前面流程来说无关紧要,所以前面的流程图中省略了,但是在这里是关键点,我们重点说说:
  那位同事的问题正好是走到这段代码,发现第二级缓存 和 原始对象不相等,所以抛出了循环依赖的异常。
  如果这时候把TestService1改个名字,改成:TestService6,其他的都不变。  @Service publicclass TestService6 {      @Autowired     private TestService2 testService2;      @Async     public void test1() {     } }
  再重新启动一下程序,神奇般的好了。
  what? 这又是为什么?
  这就要从spring的bean加载顺序说起了,默认情况下,spring是按照文件完整路径递归查找的,按路径+文件名排序,排在前面的先加载。所以TestService1比TestService2先加载,而改了文件名称之后,TestService2比TestService6先加载。
  为什么TestService2比TestService6先加载就没问题呢?
  答案在下面这张图中:
  这种情况testService6中其实第二级缓存是空的,不需要跟原始对象判断,所以不会抛出循环依赖。
  DependsOn循环依赖
  还有一种有些特殊的场景,比如我们需要在实例化Bean A之前,先实例化Bean B,这个时候就可以使用 @DependsOn 注解。 @DependsOn(value = "testService2") @Service publicclass TestService1 {      @Autowired     private TestService2 testService2;      public void test1() {     } } @DependsOn(value = "testService1") @Service publicclass TestService2 {      @Autowired     private TestService1 testService1;      public void test2() {     } }
  程序启动之后,执行结果:  Circular depends-on relationship between "testService2" and "testService1"
  这个例子中本来如果TestService1和TestService2都没有加 @DependsOn 注解是没问题的,反而加了这个注解会出现循环依赖问题。
  这又是为什么?
  答案在 AbstractBeanFactory 类的doGetBean 方法的这段代码中:
  它会检查dependsOn的实例有没有循环依赖,如果有循环依赖则抛异常。  4.出现循环依赖如何解决?
  项目中如果出现循环依赖问题,说明是spring默认无法解决的循环依赖,要看项目的打印日志,属于哪种循环依赖。目前包含下面几种情况:
  生成代理对象产生的循环依赖
  这类循环依赖问题解决方法很多,主要有:  使用 @Lazy 注解,延迟加载 使用 @DependsOn 注解,指定加载先后关系 修改文件名称,改变循环依赖类的加载顺序  使用@DependsOn产生的循环依赖
  这类循环依赖问题要找到 @DependsOn 注解循环依赖的地方,迫使它不循环依赖就可以解决问题。 多例循环依赖
  这类循环依赖问题可以通过把bean改成单例的解决。  构造器循环依赖
  这类循环依赖问题可以通过使用 @Lazy 注解解决。
  转载自:苏三说技术公众号
  原文链接:
  https://mp.weixin.qq.com/s/VpCt49_Li35caK5IaQTuNg
  器循环依赖
  这类循环依赖问题可以通过使用 @Lazy 注解解决。
  转载自:苏三说技术公众号
  原文链接:
  https://mp.weixin.qq.com/s/VpCt49_Li35caK5IaQTuNg

2021年游戏日报中秋礼盒开箱之友谊时光时光与你,月来悦好导语此栏目为游戏日报特色栏目2021年互联网游戏公司中秋礼盒开箱栏目,本期游戏日报君为大家带来的是友谊时光科技股份有限公司的礼盒展示。作为国内唯一一家上市的女性向游戏厂商,他们的中2021游戏日报中秋礼盒开箱之龙图游戏两大定制礼,内藏神器导语此栏目为游戏日报特色栏目2021年互联网游戏公司中秋礼盒开箱栏目,本期游戏日报君为大家带来的是龙图游戏的礼盒展示。龙图游戏这个手提袋就不说了,亮黑色透明袋高端大气,即便是日常提新版本爆料中秋活动即将开启,瑶月仙子和月乐园限时上线小伙伴们大家好!我是兔美美今天兔美美依旧化身新版本情报员,继续给大家带来新版本的最新爆料!中秋将至,新版本中秋活动即将开启,新装扮与月乐园限时上线!现在就跟随兔美美来一起看看,迷你恐龙大灭绝为何鸟类幸免于难?方舟生存进化里就有答案6500万年之前,一颗陨石的落下使得称霸地球数亿年之久的恐龙灭绝,可是那一次的灾难并不是灭绝了所有的生物的,有一些生物依旧存在,鸟类就是鲜明的例子。现在有研究发现,许多鸟类的基因和热血传奇名人传小雨情人!效仿8L转区征战小虾米,结局令人意外其实热血传奇中关于小雨情人的记载少之又少,大部分知道小雨情人这个名字的玩家,唯一清楚的就是他终结了,小虾米守了长达六年的沙城神话!而且他本身不是光芒区的玩家,他转区的目的就是为了狙快快加入战舰世界,领取新年战斗奖励吧欢乐的时光总是过的飞快,转眼间元旦假期已至尾声。新年的节日乐趣还在持续,回想假期里的娱乐项目,涉及方方面面,周边的商场都逛了一圈,电影院也是高朋满座。其实玩什么不重要,关键是和谁一热门游戏预告新年新气象!腾讯暴雪米哈游开年首个大招本周登场新的一周又开始了,本周又会有哪些好玩的新游戏等着我们呢?一起来看看吧。(一张图读懂每周游戏新动态)本周热点更新划重点CFHD剧情模式新篇章穿越火线幽灵计划正式定档穿越火线高清竞技大王者荣耀哪些英雄的名字容易叫错?芈月是miyue不是minyue我之前一直都叫错,直到有一次想查芈月玩法发现怎么也打不出来最容易叫错名字的5个英雄,据说90的玩家都叫错过,你中招了吗?上课了!课堂小解说来讲课了!上古神话中,犼的战力就连女娲都畏惧,为何后来却沦为坐骑?原著党答一波。注意,这个原著是指的封神而非西游,原因就是西游中女娲从来没有出场过。当然,也要用到一些别的典籍。先要强调的是,犼的战力的确强大,不过在女娲面前就是个渣而又正因为它太强LOL为什么不出个类似拉比克的英雄,偷对方技能?谢邀关注黎明看游戏带给你最新鲜,最好玩,最有深度的游戏见闻。哈哈,提这个问题的水友一定是玩DOTA的,因为在我们DOTA圈里有个叫法,我们把腾讯的总裁马花疼,就叫做马比克,因为拉比你在游戏里可以吹上一年的骚操作是什么?想当年坦克世界开着E100,开局本着占便宜的想法装填一发HE,正闷头赶路。突然远处一辆XX在山头点亮了我们这一队,双方都有几门火炮,这平原可没地躲啊,不敢停车行进中手忙脚乱对那XX
和坦克一起狂欢,阿诺德施瓦辛格让你的假日不同寻常坦克世界作为一款以坦克对战为主要内容的射击类网游,自从在国内上线后一直深受广大军迷朋友的喜爱。在很多新手玩家刚刚进入坦克世界时,留下最深刻的印象的是哪辆战车呢?KV1?小编在刚刚进iQOONeo5S兼顾高帧低耗,助力电竞级手游北京时间11月7日凌晨,冰岛,EDG战队最终以3比2的成绩成功夺得2021年英雄联盟全球总决赛的冠军宝座,一霎间全网沸腾。一个月之后,12月8日,第二届北京大学生电竞节,中传和虎牙梦幻模拟战手游巅峰梦幻基础知识分享作者NGAzkbeststone赶在魏老师大部队来之前总算吭哧吭哧爬上去了。这赛季初被刺客打得生活不能自理。后面慢慢顶住刺客才稳住了阵地。不得不说龙DD残局实在是太强力了。关于龙D后裔很忙魔兽争霸3梦西游2后裔核心c玩法现在在魔兽争霸官方对战平台上面,梦西游2这张地图是非常非常火热的,而且之前地图嘉年华的成功举办,让更多的玩家看到了高质量地图的可玩性是真的高,更多玩家也选择加入了进来,与其玩的多,101姆巴佩领衔!于帕新增即精选!升级后首批周精选简析直营平台手游升级v5。6。0版本以后,同步推出了基于dp8。0数据库普卡的首期周精选。而国服手游由于尚未更新,导致目前普卡数据与dp8。0数据库存在差异,因此有可能不会上架本期周精1。76传奇为何被称之为传奇的经典1。76传奇为何深受传奇老玩家喜爱哈喽,大家好呀,我是钟爱传奇的传奇老玩家小彤呀,欢迎来到小彤的传奇路。1。76版本传奇一直都被称之为传奇中的经典,也深受传奇老玩家的喜爱,那他到底有什么特别之处呢,跟着小彤来看看!福特概念赛车,坐进驾驶舱新能玩网络游戏福特旗下的福特zilla团队与HP合作,在2020年8月发表P1福特虚拟概念车,并在当年10月推出实体概念车,最近开发团队调整P1福特概念车的座舱配置,让这辆概念车成为史上最贵的电Epic神秘喜1第七天突变元年伊甸园之路免费领取Epic圣诞15天免费神秘游戏活动正式上线。发布的第七款神秘游戏是异变元年伊甸之路。异变元年伊甸之路游戏介绍异变元年伊甸之路是由TheBeardedLadies开发的一款战术角色扮想拜我为师不,荣荣送你回家泡温泉今天给大家介绍一下梦琪这个英雄,这个英雄前两个赛季被改版了。我刚开始玩这个英雄的时候,他的技能还不是这样的,技能有一定的改变,增加了灵活性!这个英雄的定位是法师坦克,价值13888英雄联盟手游2。6版本更新,有哪些玩家变动和内容呢?有关英雄联盟手游版本更新我是一路都有关注过来,相比于2。5重点推出公会和公会战等模式,提高英雄联盟手游的优化社交属性而言,2。6的版本更新感觉来的更加百花齐放。首先是新英雄,新皮肤人气满满的率土之滨,在王者荣耀面前都是小众,slg圈还是有点小对于slg策略游戏圈内,率土之滨是一款里程碑式的存在,可以说现在主流沙盘三国手游都或多或少有借鉴率土之处。即便这款游戏一直比较肝,现在又变得比以前氪金了,依然人气不低。可是在圈内如