spring循环依赖产生的情况有很多种,并且spring循环依赖一直是开发者们老生常谈的问题,遇到这个问题,就要及时去解决,否则肯定会影响开发,那下面我们就给大家讲解一下spring解决循环依赖问题方案!
首先我们要知道什么是Spring的循环依赖问题。假如我们有两个bean,A 和 B。他们的代码简单如下:
@Bean public class A { @Autowire private B b; } @Bean public class B { @Autowire private A a; }
也就是需要在A中注入B,在B中注入A,那么Spring在创建A的时候会出现这种现象:创建A实例后,在依赖注入时需要B,然后就去创建B,这时候发现又需要依赖注入 A ,这样就导致了循环依赖。
Spring对循环依赖的解决方法可以概括为 用三级缓存方式达到Bean提前曝光的目的
Spring创建的过程简单的可以简单概括为 实例化——>依赖注入——>初始化。而Spring解决循环依赖的方法就是在实例化之后,依赖注入之前,将实例化的对象放到缓存中进行提前曝光,后边的对象则在实例化前,先到缓存中查找有无对应的实例化对象即可,
源码解析:
1.首先我们创建两个类如上边的 A 和 B
2.启动Spring容器,在创建A 的过程中会先来到 AbstractBeanFactory.doCreateBean 方法中找缓存(当然第一次找,肯定没有找不到,所以 sharedInstance = null)
3.所以不会走if里边的逻辑直接往下走,走到创建获取单例实例的位置(注意这里传入 getSingleton方法的第二个参数是一个方法的lamda表达式,后边有用),进入方法
4.到了 DefaultSingletonBeanRegistry.getSingleton 方法,走到标记部分,这里就是调用了传入的 lamda 表达式的方法,进入这个方法)
5.来到了 AbstractAutowireCapableBeanFactory.createBean 方法,其中重点方法是如下标记的方法,进入
6.还是原来的类,来到了doCreateBean 方法,这里先创建并获得实例对象
7.然后走到如下标记的位置,这里就是缓存三级缓存的代码部分,
8.具体如下标识,首先判断一级缓存有无对应实例(此时没有),接着将传入的 () -> getEarlyBeanReference(beanName, mbd, bean) 放到三级缓存 singletonFactories 中,这时候三级缓存就有对象了,此时的对象只是一个 带有方法
() -> getEarlyBeanReference(beanName, mbd, bean) 的 objectFactory对象
9.放入三级缓存后,回到doCreateBean方法继续走,走到这里开始进行依赖注入
10.这里边后就需要对属性b进行注入,所以继续走前边一开始创建A的步骤一样去创建对象B,也是走到依赖注入这里,这时候要对B的属性A进行注入,所以又来到创建A的阶段 AbstractBeanFactory.doCreateBean,不过这次的 三级缓存 singletonFactories 已经有上一次放入的objectFactory了
11.进入这个方法,这里就是三级缓存拿数据的重点,其中需要说明下,三级缓存存的对象的 lamda 表达式 ( () -> getEarlyBeanReference(beanName, mbd, bean) ) 的方法,里边的逻辑上为判断对象需不需要代理,需要的话创建bean的代理并返回,不需要的话则直接返回 bean
12.所以接着就继续走下去,b能拿到对象a进行依赖注入,继续走完 AbstractAutowireCapableBeanFactory.createBean 方法(第5步),返回对象回到 DefaultSingletonBeanRegistry.getSingleton方法中(第4步),此时b的值已经有了,
继续走下去,将B放入一级缓存(单例缓存),同时移除二级缓存和三级缓存,逻辑在addSingleton()方法中,B创建完成,最后返回b对象给到一开始的a,a拿到b后就可以进行注入,注入后又将a放入一级缓存,移除二级缓存和三级缓存。至此A也创建好了。
以上就是Spring解决循环依赖的具体源码实现,其实解决Spring循环依赖的主要方式就是三级缓存,另外,循环依赖也是可以检测出来的, 可以 Bean在创建的时候给其打个标记,如果递归调用回来发现正在创建中的话,就说明循环依赖。最后大家如果想要了解更多java架构师知识,敬请关注奇Q工具网。
推荐阅读: