寄生虫做网站流量,怎么做彩票平台网站,设计集团有限公司,广告灯箱设计制作价格作者#xff1a;Vt前言Spring如何解决的循环依赖#xff0c;是近两年流行起来的一道Java面试题。其实笔者本人对这类框架源码题还是持一定的怀疑态度的。如果笔者作为面试官#xff0c;可能会问一些诸如“如果注入的属性为null#xff0c;你会从哪几个方向去排查”这些场景…作者Vt前言Spring如何解决的循环依赖是近两年流行起来的一道Java面试题。其实笔者本人对这类框架源码题还是持一定的怀疑态度的。如果笔者作为面试官可能会问一些诸如“如果注入的属性为null你会从哪几个方向去排查”这些场景题。那么既然写了这篇文章闲话少说发车看看Spring是如何解决的循环依赖以及带大家看清循环依赖的本质是什么。正文通常来说如果问Spring内部如何解决循环依赖一定是单默认的单例Bean中属性互相引用的场景。比如几个Bean之间的互相引用甚至自己“循环”依赖自己先说明前提原型(Prototype)的场景是不支持循环依赖的通常会走到AbstractBeanFactory类中下面的判断抛出异常。if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName);}原因很好理解创建新的A时发现要注入原型字段B又创建新的B发现要注入原型字段A...这就套娃了, 你猜是先StackOverflow还是OutOfMemorySpring怕你不好猜就先抛出了BeanCurrentlyInCreationException基于构造器的循环依赖就更不用说了官方文档都摊牌了你想让构造器注入支持循环依赖是不存在的不如把代码改了。那么默认单例的属性注入场景Spring是如何支持循环依赖的Spring解决循环依赖首先Spring内部维护了三个Map也就是我们通常说的三级缓存。笔者翻阅Spring文档倒是没有找到三级缓存的概念可能也是本土为了方便理解的词汇。在Spring的DefaultSingletonBeanRegistry类中你会赫然发现类上方挂着这三个MapsingletonObjects 它是我们最熟悉的朋友俗称“单例池”“容器”缓存创建完成单例Bean的地方。singletonFactories 映射创建Bean的原始工厂earlySingletonObjects 映射Bean的早期引用也就是说在这个Map里的Bean不是完整的甚至还不能称之为“Bean”只是一个Instance.后两个Map其实是“垫脚石”级别的只是创建Bean的时候用来借助了一下创建完成就清掉了。所以笔者前文对“三级缓存”这个词有些迷惑可能是因为注释都是以Cache of开头吧。为什么成为后两个Map为垫脚石假设最终放在singletonObjects的Bean是你想要的一杯“凉白开”。那么Spring准备了两个杯子即singletonFactories和earlySingletonObjects来回“倒腾”几番把热水晾成“凉白开”放到singletonObjects中。闲话不说都浓缩在图里。上面的是一张GIF如果你没看到可能还没加载出来。三秒一帧不是你电脑卡。笔者画了17张图简化表述了Spring的主要步骤GIF上方即是刚才提到的三级缓存下方展示是主要的几个方法。当然了这个地步你肯定要结合Spring源码来看要不肯定看不懂。如果你只是想大概了解或者面试可以先记住笔者上文提到的“三级缓存”以及下文即将要说的本质。循环依赖的本质上文了解完Spring如何处理循环依赖之后让我们跳出“阅读源码”的思维假设让你实现一个有以下特点的功能你会怎么做将指定的一些类实例为单例类中的字段也都实例为单例支持循环依赖举个例子假设有类Apublic class A { private B b;}类Bpublic class B { private A a;}说白了让你模仿Spring假装A和B是被Component修饰 并且类中的字段假装是Autowired修饰的处理完放到Map中。其实非常简单笔者写了一份粗糙的代码可供参考 /** * 放置创建好的bean Map */ private static Map cacheMap new HashMap(2); public static void main(String[] args) { // 假装扫描出来的对象 Class[] classes {A.class, B.class}; // 假装项目初始化实例化所有bean for (Class aClass : classes) { getBean(aClass); } // check System.out.println(getBean(B.class).getA() getBean(A.class)); System.out.println(getBean(A.class).getB() getBean(B.class)); } SneakyThrows private static T getBean(Class beanClass) { // 本文用类名小写 简单代替bean的命名规则 String beanName beanClass.getSimpleName().toLowerCase(); // 如果已经是一个bean则直接返回 if (cacheMap.containsKey(beanName)) { return (T) cacheMap.get(beanName); } // 将对象本身实例化 Object object beanClass.getDeclaredConstructor().newInstance(); // 放入缓存 cacheMap.put(beanName, object); // 把所有字段当成需要注入的bean创建并注入到当前bean中 Field[] fields object.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); // 获取需要注入字段的class Class fieldClass field.getType(); String fieldBeanName fieldClass.getSimpleName().toLowerCase(); // 如果需要注入的bean已经在缓存Map中那么把缓存Map中的值注入到该field即可 // 如果缓存没有 继续创建 field.set(object, cacheMap.containsKey(fieldBeanName) ? cacheMap.get(fieldBeanName) : getBean(fieldClass)); } // 属性填充完成返回 return (T) object; }这段代码的效果其实就是处理了循环依赖并且处理完成后cacheMap中放的就是完整的“Bean”了这就是“循环依赖”的本质而不是“Spring如何解决循环依赖”。之所以要举这个例子是发现一小部分盆友陷入了“阅读源码的泥潭”而忘记了问题的本质。为了看源码而看源码结果一直看不懂却忘了本质是什么。如果真看不懂不如先写出基础版本逆推Spring为什么要这么实现可能效果会更好。what问题的本质居然是two sum看完笔者刚才的代码有没有似曾相识没错和two sum的解题是类似的。不知道two sum是什么梗的笔者和你介绍一下two sum是刷题网站leetcode序号为1的题也就是大多人的算法入门的第一题。常常被人调侃有算法面的公司被面试官钦定了合的来。那就来一道two sum走走过场。问题内容是给定一个数组给定一个数字。返回数组中可以相加得到指定数字的两个索引。比如给定nums [2, 7, 11, 15], target 9 那么要返回 [0, 1]因为2 7 9这道题的优解是一次遍历HashMapclass Solution { public int[] twoSum(int[] nums, int target) { Map map new HashMap(); for (int i 0; i 先去Map中找需要的数字没有就将当前的数字保存在Map中如果找到需要的数字则一起返回。和笔者上面的代码是不是一样先去缓存里找Bean没有则实例化当前的Bean放到Map如果有需要依赖当前Bean的就能从Map取到。结尾如果你是上文笔者提到的“陷入阅读源码的泥潭”的读者上文应该可以帮助到你。可能还有盆友有疑问为什么一道“two-sum”Spring处理的如此复杂 这个想想Spring支持多少功能就知道了各种实例方式..各种注入方式..各种Bean的加载校验..各种callbackaop处理等等..Spring可不只有依赖注入同样Java也不仅是Spring。如果我们陷入了某个“牛角尖”不妨跳出来看看可能会更佳清晰哦。来源掘金 链接https://juejin.im/post/5e927e27f265da47c8012ed9