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

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

育碧推出NFT平台,在游戏里引入了区块链系统结果遭到玩家强烈抵制。今年,不少大型游戏厂商都对近来很火的NFT(非同质化代币)概念公开表示过感兴趣,而育碧成为了游戏圈第一个吃螃蟹的大型公司。昨天,育碧推出了自己的Ubisoft游戏行业重磅年会即将召开,中信称或将指引政策走向12月14日到16日在广州召开。今日重要性据中国音数协游戏工委此前披露,2021年度游戏产业年会将于12月14日到16日在广州召开。资料显示,游戏产业年会自2004年起每年底召开一索尼公布战神4PC版配置要求中配GTX10601070显卡IT之家12月9日消息,由索尼PlayStation发行的游戏战神4(2022年1月15日发售)即将登陆PC平台,将于2022年1月15日正式发售,定价279元。今日官方公布了这款传承与开拓,破晓传说的五年之约本文作者雾鸦2021对于游戏玩家们而言,恐怕过得相当煎熬,年初赛博朋克2077掀起的风浪久久不能散去,年末的使命召唤先锋,战地2042,侠盗猎车手三部曲重制版又接连翻车,仿佛玩家们当游戏引擎遇上豪华智能汽车,高合丁磊开始推动行业变革据埃森哲估计,全球游戏行业的总价值现已超3000亿美元,超过了电影和音乐市场的总和。在过去三年,游戏行业玩家人数增加了10亿,全球共有27亿玩家。集社交文化娱乐科技于一体的游戏正在争论二十年的话题,云和山的彼端天之痕到底哪款才是巅峰轩辕剑系列你最喜欢的版本是哪一款呢?其实这还真不好说,玩过12枫之舞的小伙伴很有可能先入为主就喜欢DOS时代的版本。毕竟嘛!时代造就经典。而轩辕剑3云和山的彼端和轩辕剑3外传天之痕还我剧本杀复盘真相凶手是谁谋杀之谜需要复盘请私信小编,小编看到后第一时间回复您哦。故事类型还原微恐立意标准人数玩家6人DM1人测评时间45h测评分数恐怖还原推理无剧透点评奋前期还我还原的份量会比较重,但推理的并非是东半球最强法务败北!老任eShop预购退款政策或将改动任天堂的法务部因为专业太强,被大家笑称为宇宙最强法务部,不过这个宇宙最强法务部这次也遭遇了滑铁卢!上周五,任天堂因eShop预购退款政策在德国一家法院败诉。法庭上,德国消费者组织联削弱后的貂蝉有多失败?张老三直播怼到破防,国一都知道没救了看到标题相信不少玩家都知道了,继花木兰元歌李白后有一个高难度英雄遭遇到了策划的针对,被史诗级别的削弱了,那就是貂蝉。王者荣耀再次削弱貂蝉,使得不少玩家甚至是国服玩家都怨声载道,也让为什么现在的青年可以为EDG夺冠狂欢?2000年,你出生了。在一个中产阶级的家庭中,爸爸妈妈都很爱你。你慢慢长大了,三四岁的时候,你看着电视里的动画片自娱自乐,因为父母都在工作,没有时间陪你一起玩。等在大了一点,你可以嘴强王者迎用武之地,王者赛事教练上线,预测比赛BP走向随着KPL联赛和王者荣耀的发展,玩家的游戏理解也逐渐加深。尽管职业战队对于我们普通玩家来说是可望不可即的事情,可是我们对于比赛的BP也是有着自我的理解。在每次王者荣耀的职业比赛中,
XYG败给TTG止步四强,XYG教练接受采访粉丝懵了!九月说好不拼惩在最近的KPL中,四强战队也是确定了为ES狼队TTGXYG,由于当时在S组的排名关系,所以XYGTTG注定要比其他队伍走的更困难一些,但这两支队伍都顶住了,特别是XYG,从K甲一路云顶之弈白魔保镖狙火了,最高52的吃鸡率,又一个毒瘤诞生阵容决定下限,装备决定上限,运营才是王道!弈士们好,我是你们的老朋友摩昂,今天摩昂给大家带来的是现在版本吃鸡率最高的阵容白魔保镖精密狙!白魔保镖狙阵容是热补丁后热门体系中没有被削弱2021德玛西亚杯HYA小超梦上路一打五!焰轩两局比赛一次不死在今天进行的德玛西亚杯A组比赛中,WE对阵HYA虎牙主播队!比赛前期HYA主播队打的有来有回,比赛中期运营和思路还出现了问题,最终不敌WE战队。但是HYA的上路小超梦打的很不错,在威客电竞LOLBLI01V5,碾压取胜Game1蓝色方BLI,红色方V5BanPickBLI禁用盲僧,奥拉夫,伊泽瑞尔,加里奥,格温BLI选用xiaonabao鳄鱼lizheng人马LYE辛德拉hanbaowang女警幻塔亲测说说今天游戏的体验幻塔第一天公测,也是感慨良多?有的小伙伴已经玩上了,有的还没玩上?今天也是周四上班日子,游戏早上8点开始,但不少人还没来得及玩。1登陆为了能第一时间玩到幻塔,我早早就等候,好能开服最终幻想14国际服玩家爆满游戏暂时停售近日,最终幻想14制作人吉田直树对国际服再次出现大规模拥堵情况致歉,并宣布将暂时停售游戏。在官网公告中,吉田直树表示目前服务器无力支持那么多玩家同时在线,他们将暂时停售游戏本体及资Steam海盗游戏ATLAS出现小型货运船?航速快还很坚固海盗游戏ATLAS是在Steam平台上线的一款生存沙盒游戏,目前这款游戏上线已有3年时间,凭借着独特的航海题材和多元化的沙盒玩法,深受全球不少玩家的喜爱。在游戏内,玩家可以体会到真复古传奇打怪不爆装备?以前弯路走多了就明白了一些老G挖的坑当年玩传奇的时候一直吐槽有些怪物打完了都不出装备现在才明白原来不出装备的话是可以挖出装备的其实新手村进来都已经非常明显地提示玩家有些怪物是要挖的最经典莫过于挖鸡肉鹿肉但是到了后面大复古传奇土豪玩战士只会无脑冲在最前面?对于战士最深刻的理解土豪毫无疑问都会选择玩战士他的外观霸气侧漏近身博弈的快感三职业中他永远是冲在最前面的那个人有一种视死如归大无畏的精神虽然它没有法师的技能多样也没有远攻技能但是给人更加真实的感觉战士永劫无间劫杯第三周次日Ventus暂居榜首,奈河桥地位无可撼动永劫无间中国赛区预选赛入围赛第三周第二日的比赛已经完美落幕,Ventus战队以积分暂居榜首,KN战队排名第二。不过在击败排行榜上,前两名依旧是KN战队的奈河桥和子鱼,真的是稳居第一影子战术爱子的选择评测武士悲歌的回眸一瞥作者NGAhjyx01爱子的过去与决战的前夜前不久推出的影子战术爱子的选择是作为RTT知名佳作影子战术将军之刃可独立运行的DLC存在,讲述了在影子战术将军之刃最后的任务刺杀影之前,