专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

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

甄嬛传淳儿是谁的人?攀附甄嬛,其实是在为祺贵人铺路甄嬛传中的淳常在是个简单善良的女孩,进宫的时候不过14岁,因为跟自己妹妹的年纪相仿,甄嬛很是喜欢她。恰好甄嬛当时圣意正浓,淳儿也跟着沾了光,成了皇帝最宠爱的妃嫔之一,可恰恰就是因为鱿鱼游戏拔河比赛是拼力气,幕后BOSS老头如何保障自己的性命?鱿鱼游戏是当下最为热门的一部作品,整部电视剧将人性的丑恶展露得淋漓尽致,把童年游戏跟欲望紧紧联系在一起,一个小失误就有可能丧失性命。而这些游戏的创始人则是一个毫不起眼的老头,耗费重鱿鱼游戏老人是游戏主办者,他为何要按红灯终止游戏?想必看过鱿鱼游戏的观众都知道,001号参赛者是全场游戏的主办者,谁都没想到,这个白发苍苍,看上去面容和善的老人背地里竟如此的心狠手辣,好在游戏规则里有一项规则,只要超过半数的参赛者鱿鱼游戏成奇勋赢了,为何还要参加下一轮游戏?其实他是被迫的鱿鱼游戏中的成奇勋是个一无是处的人,欠了一屁股债,还得靠自己老妈救济,连给女儿过生日的钱都拿去堵马了。最终用几个巴掌换回来10万韩元,还喜滋滋想跟老妈一起分享。本以为成奇勋这辈子就鱿鱼游戏智英的故意认输,跟姜晓无关,她本就没打算活着出去要说整部鱿鱼游戏中最好哭的一个场景,无非是弹珠游戏了。因为大家选择的基本都是很重要的朋友,可游戏的规则不是让他们合作,而是利用弹珠杀了对方。横竖都是死,就看大家是爱自己还是更爱朋友三星note20超大杯值得买吗?三星华为手机游戏港版三星Note205G手机,三星note20选择只有两个,但也不困难,大杯和超大杯。不是人傻钱多,而是大杯实在不值得啊,都7000多了,你选一个屏幕只有1080P虽然我不反对他打球,但这就有点过分了篮球科比体育夫妻感情他是个篮球迷,各种篮球鞋都快200双了,一到NBA赛季,关键比赛基本都要看直播。平时还喜欢收集各种篮球海报,明星模型,手办之类的。这种爱好我也没反对,毕竟他不抽我和我的家乡,这怕是近期最值钱的电影这种级别阵容,这么多的明星和实力派演员不说空前绝后,但真的是少见了。这个可能还不是最主要的关键是这部电影的水准,那还是相当之高的。周五个小短片水准,虽然有些参差不齐,但总体还是非常夫妻开车出行,另外一半不要坐副驾驶!开车夫妻夫妻那些事国庆交通随着汽车越来越普及,夫妻开车出行旅游已经是一种越来越普通的方式,那么谁坐副驾驶呢?先说说身边的例子。有一年我和朋友出国旅游,当时我还没学会开车。所以一下飞参加家长会后,想的心都有家长孩子与教育儿童和老婆一起参加一年级家长会,参加完后,老师招手把我们叫了过去。年轻的班主任表情复杂的看了我一眼,说你儿子可太调皮了,这事我都没法开口,你回避一下,我和你夫人说吧。NBA第一人,詹姆斯?乔丹?科比?这三人都是NBA赫赫有名的人物,尤其是詹姆斯以30多岁的高龄再次夺冠,继续书写他的彪悍人生,简直是活着的传奇。科比乔丹那就不多说了,家喻户晓的人物。那么他们三谁才是NBA第一人呢?
魔域口袋版九尾狐幻兽AI语音撩你心弦单体强控纵横战场七夕将至又到了情侣们成双入对的时节,而在魔域口袋版中征战的你是否也想在亚特大陆找寻到你的完美伴侣?全新九尾狐幻兽青丘帝姬九璃即将魅惑登场美艳无双的颜值势必让你一见倾心最强单体控制技狐妖小红娘手办免费送!英魂之刃口袋版联动短视频大赛开启万水千山,燃爱相约!英魂之刃口袋版狐妖小红娘联动短视频大赛火热进行中。当帅气逼人的英雄角色碰上呆萌可爱的狐妖小红娘,会碰撞出怎样的火花呢?即日起至9月17日,参与英魂之刃口袋版狐妖魔道至尊侠客风云传OL青衣剑仙飘然而来不疯魔,不成活,作为侠客风云传OL里的颜值担当,魔荆棘凭借俊朗的外形和强悍的手段,在江湖中犹如人中之龙一般的存在,女性角色中,青衣剑仙以其娟秀瑰丽,元气满满的小仙女气质,自然也成为爆料来了熔火分身,爆裂流星!魂匣泰坦熔火之魂火焰冲击在部族之争的最后阶段,战败方仅存的征战部队被压制至火山的最深处,大法师在临危的关头利用召唤法术汇聚了此处的能量,创造出了此等元素生物。当大法师见识到这个元素生物的威力时,感到无比震20年IP再起航破天世界正式开启预约让各位少侠久等了!!!新一代破天手游破天世界预约正式开启!由破天一剑端游和破天一剑手游原班人马联合开发,保留游戏经典的同时在建模美术玩法等全面升级传承经典破天IP,重拾最初的武侠情化身月狮航海王燃烧意志战兽民族SSR凯罗特登场她来自佐乌默克莫公国,是一名纯毛族,这个民族被称为天生的战士。不仅如此,她还作为火枪手团的一员,接受过高层次的训练,其实力无法言喻,她就是默克莫公国的火枪队队员凯罗特。凯罗特一直希魔域口袋版首只AI语音幻兽曝光一起来找乐子吧这个夏天,一场激情与福利交织的冒险让魔域口袋版的玩家们过足了瘾全新资料片千里江山开创沉浸式玩法新篇章,游历南国感受风情万种踏足大荒寻获至宝无数。而如今探索的步伐又将迈向大荒之中的青永定土楼联动皮肤登场英魂之刃口袋版杨戬攻略放送天神降临,为的不是征服,而是守护。7月30日,英魂之刃口袋版永定土楼联动皮肤杨戬青羽天神已经正式上线啦。天神杨戬是一名强大的团战英雄,技能效果会因为敌人的增加而增强。要想知道如何才斗鱼CJ直播间百大主播轮番带你逛展更有豪礼送不停今年2021年的CJ展会在上海举办,每年的CJ基本上是游戏动漫爱好者的盛宴。此次由于疫情影响,现场人数略有减少,大家对于CJ的关注和喜爱一点没有减少。随着ChinaJoy展会在中国边锋bfun在出海中完成产品进化后疫情时代,宅经济迅猛发展,休闲游戏凭借易上手满足碎片化娱乐需求的优势,成为近两年占据国内用户游戏时长最多的品类,据易观分析统计,以棋牌消除类为主的休闲游戏,2020年1月占据用户边锋网络推进未成年人保护开拓海外新赛道随着新修订的未成年人保护法落地实施,未成年人保护再度成为社会关注的焦点,特别是新版未成年人保护法增设的网络保护专章,对未成年人防沉迷净化未成年人网络环境提供了更好的指导和保护,边锋
友情链接:快好知快生活快百科快传网中准网文好找聚热点快软件