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

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

Uzi粉丝战斗力太强,三天还不肯罢休,爆破BLG官博让Mark道歉Uzi可以说是LPL人气最高且争议最多的选手,粉丝和黑粉都很多,只要跟Uzi扯上关系,就算是很小的事情也能引发节奏,LPL有不少选手都吃过这个亏。Uzi自带舆论节奏比如说之前JDGMSI的伤心人,PGG被虐后发文回应RNG把我们认错成PSG了MSI对抗赛进行到一半,所有队伍都打了六场比赛,这下没有队伍能够全胜了,RNG和DK各自输了一场,不过对于他们的晋级没有太大影响,总不可能接下来的比赛全输。虽然没有全胜的队伍,但对Uzi影响力有多大?复出的消息传到海外,北美大师兄都在关注他在LPL众多退役选手里,Uzi是最让人惋惜的一个,明明还有很好的竞技状态,可他却因为伤病退役,从上一年六月份到现在,Uzi退役已经超过一年虽然Uzi已经退役,但LPL依然流传着他的LOL手游解说面试门槛太高,记得不满足条件,又回来LPL了LOL手游发展速度很快,即使目前还在公测阶段,拳头公司也已经在准备搭建赛事,等LOL手游全面上线时,职业联赛必定随之而来。既然有比赛,那么就需要解说,目前拳头官方已经在招募LOL手S12大变天,PDD爆料除了Uzi还有很多人转会,RNG一个都买不起LPL夏季赛常规赛才开启第四周时间,但对于一些队伍来说已经可以准备S12的工作了,比如说IG,Theshy因为签证影响最近才回到上海,目前还在隔离中,没有Theshy的IG就是一支小虎练出新英雄,RNG养虎战术又来了,呼吸哥变成窒息哥自从小虎转型上单后,RNG就开辟了一套养虎战术,春季赛以及MSI季中赛无往不利,各路好手都倒在RNG脚下,凭借这一套战术RNG也成功收获上半年两个冠军。不过来到夏季赛后RNG出现挫使命召唤手游体验,CODM为什么能收获如此的好评与盛赞?这几天最火热的手游我说是使命召唤手游我想很多玩家都会认同吧,CODM国服上线后不久就收获大量玩家的好评和盛赞,在FPS手游圈中,基本都能看到在讨论CODM的玩家,而CODM为什么能一致对外!LPL出征队伍集合,EDGFPXRNGLNG冲刺第三个冠军LPL出征队伍集合完毕随着LNG击败WE,LPL赛区的四支世界赛队伍正式决出,EDG一号种子,FPX二号种子,RNG三号种子,LNG四号种子,前三支队伍直接进入小组赛,LNG则要从别想了,RNG世界赛不会租借Knight,拳头统一办理选手签证随着LNG击败WE,LPL赛区的冒泡赛正式结束,世界赛队伍也全部出来,EDG一号种子,FPX二号种子,RNG三号种子,LNG四号种子。接下来就是世界赛的准备时间,LPL也没有比赛打一诺抢队友五杀,笑影却发文感谢,半肉上官婉儿看不到了世冠杯打到第三周,强队和弱队的区别已经体现出来,KPL队伍在KPL队伍面前就跟小孩子一样,对线打不赢,运营差距更加大,AG超玩会三场比赛全胜,已经提前锁定八强名额。AG超玩会第三个LPL季后赛分区完成,上半区堪称死亡之组,下半区EDG一家独大随着FPX二比一击败EDG,常规赛也宣告结束,接下来就是季后赛的争夺,10支队伍都朝着冠军的方向走,尤其最后还要冲击世界赛名额。由于LPL季后赛采用双败制,因此10支队伍也被分成上
原神2。4版本的好消息和坏消息好消息想要的角色在2。4坏消息想要的角色都在2。4坏消息新地图怪物好可怕好消息钟离老爷子复刻了!坏消息原石不够了好消息富贵登门送十连抽!坏消息新地图的宝箱不好找好消息宝箱有原石坏消KPL最佳阵容评选结果出炉,eStar五人占一套,心疼TTG冰尘整个王者荣耀KPL秋季赛已经打完了,在经过联盟的一番焦灼的商讨下,本届KPL最佳阵容的选手名单已经出来了,令人感到意外的是,这一次KPL联盟评选出了两套最佳阵容,分别是第一最佳阵容原神4年后的今天,崩3情景再现,多角色服饰被迫更改各位好呀,我是助手。2018年1月18日,正当玩家享受2。0春节版本时,崩3发布了一则公告,暂时关闭了角色触摸系统,官方以1000水安抚玩家的情绪,其实玩家也知道,关闭触摸并不是忽原神2。4版本必做任务清单,极限攒1。4w原石,甘雨小保底有了好游快爆消息原神已于1月5日更新2。4版本飞彩镌流年,想要玩转2。4版本?前往快爆获取新版本角色攻略,还有地图资源查询器等实用工具攻略助你探索渊下宫新地区!2。4版本终于来啦大家做盘点跨界玩斗地主最好的主播,王者荣耀吕德华这次实至名归了斗地主这个游戏,不少人都会玩,毕竟这个充满未知和变数的棋牌游戏,玩的除了猜牌的脑力,还有给对手施压和抗压的心理能力,的确是一款易学难精的棋牌游戏。对于这种有变数的棋牌游戏,主播们当2022虎牙星盛典Miss鲨鱼哟确认参加,张大仙未官宣,却占了C位前段时间,虎牙年度比赛圆满结束。经过几轮比赛,网友选择出了年度主播。例如游戏方面,曹操大表哥QUQUJY等主播纷纷上榜。虽然这次上榜的都是新面孔,但可能也只是大家不太熟悉的原因,其剑侠世界3中枪出如龙的霸气,天王角色够飒剑侠世界3自从上线以来,玩过不少的角色,可以说每一个角色都有自身的特点,有PK能力强大的天忍,也有适合刷副本的武当,还有辅助能力比较强的易水,而最近体验了一下霸气的天王,确实够飒的北慕赛季末登顶巅峰赛,小锦儿微凉难受了!一周比一个月上分都快在最近已经赛季末,如果按照往常的赛季那么这个时候基本已经有了定数,就算是S24的陌辰和微凉较量时,微凉也是有着想当大的优势。但在S25赛季如今巅峰赛第一的位置还未确认,而在最近北慕又一个脆皮的梦魇上线!飞檐走壁无视护甲直接斩杀的刺客上线大家好,我是大宝备。新英雄暃即将新赛季正式登场,这个英雄大宝备认为是一个操作简单但是又能秀的刺客打野,而且大宝备还认为这个英雄强度极高,所以今天给大家带来这个英雄的详细玩法教学。技S26赛季到来,新英雄暃的出装铭文连招,你知道了吗看到了很多关于新英雄暃的出装,但真的不知道该用哪一套。今天小编就帮大家整理了两套体验服已测试适合暃的出装,铭文该怎么搭配最实用,最好用的连招顺序是什么,总感觉他的技能很僵直,怎么样ES花海比赛巅峰赛双开,抽空都能冲第一,小号都冲到了前十?说起武汉eStarPro战队的花海相信打野都不陌生吧,作为冠军打野花海的实力是有目共睹的,同样因为成绩不错武汉eStarPro战队的比赛也比较多,不管是KPL的比赛还是最近挑战者杯