当前位置:蜗牛素材网>综合资讯>图文>正文

spring怎么发现循环依赖?是如何解决循环依赖的

人气:415 ℃/2023-12-29 00:46:19

前言

在面试的时候这两年有一个非常高频的关于spring的问题,那就是spring是如何解决循环依赖的。这个问题听着就是轻描淡写的一句话,其实考察的内容还是非常多的,主要还是考察的应聘者有没有研究过spring的源码。但是说实话,spring的源码其实非常复杂的,研究起来并不是个简单的事情,所以我们此篇文章只是为了解释清楚Spring是如何解决循环依赖的这个问题。

什么是循环依赖?

循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:

循环依赖有哪几种方式?setter方式 - 单例

/** * A服务类 * * @author liudong * @date 2021/12/20 14:48 */@Service("aService")public class AService { @Autowired public BService bService;}

/** * B服务类 * * @author liudong * @date 2021/12/20 14:54 */@Service("bService")public class BService { @Autowired public AService aService;}

/** * 配置类 * * @author liudong * @date 2021/12/20 14:59 */@Configuration@ComponentScanpublic class AppConfig {}

/** * 测试应用类 * * @author liudong * @date 2021/12/20 14:56 */public class TestApplication { public static void main(String[] args) { ApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class); AService aService = (AService) annotationConfigApplicationContext.getBean("aService"); System.out.println(aService.bService); BService bService = (BService) annotationConfigApplicationContext.getBean("bService"); System.out.println(bService.aService); }}

执行结果:

com.gaodun.spring.BService@1ed6388acom.gaodun.spring.AService@5a45133e

虽然AService和BService产生了循环依赖的关系,但是没有异常抛出。说明被Spring解决了。但是能够通过“setAllowCircularReferences(false)”来禁用循环引用。这样也会抛出BeanCurrentlyInCreationException异常!测试代码如下:

/** * 测试应用类 * * @author liudong * @date 2021/12/20 14:56 */public class TestApplication { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.setAllowCircularReferences(false); applicationContext.register(AppConfig.class); applicationContext.refresh(); AService aService = (AService) applicationContext.getBean("aService"); System.out.println(aService.bService); BService bService = (BService) applicationContext.getBean("bService"); System.out.println(bService.aService); }}

执行结果:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'aService': Requested bean is currently in creation: Is there an unresolvable circular reference?

需要注意的是,我们不能这么写:

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

applicationContext.setAllowCircularReferences(false);

如果你这么写,程序执行完第一行代码,整个Spring容器已经初始化完成了,你再设置不允许循环依赖,也于事无补了。

setter方式 - prototype

使用@Scope(BeanDefinition.SCOPE_PROTOTYPE),进行多例的setter注入 ,每次请求都会建立一个实例对象。

/** * A服务类 * * @author liudong * @date 2021/12/20 14:48 */@Service("aService")@Scope(BeanDefinition.SCOPE_PROTOTYPE)public class AService { @Autowired public BService bService;}

/** * B服务类 * * @author liudong * @date 2021/12/20 14:54 */@Service("bService")@Scope(BeanDefinition.SCOPE_PROTOTYPE)public class BService { @Autowired public AService aService;}

执行结果:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'aService': Requested bean is currently in creation: Is there an unresolvable circular reference?

对于“prototype”作用域bean,Spring容器没法完成依赖注入,由于Spring容器不进行缓存“prototype”做用域的bean,所以没法提早暴露一个建立中的bean。

构造器循环依赖

/** * A服务类 * * @author liudong * @date 2021/12/20 14:48 */@Service("aService")public class AService { public BService bService; public AService(BService bService) { this.bService = bService; }}

/** * B服务类 * * @author liudong * @date 2021/12/20 14:54 */@Service("bService")public class BService { public AService aService; public BService(AService aService) { this.aService = aService; }}

执行结果:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'aService': Requested bean is currently in creation: Is there an unresolvable circular reference?

通过构造器注入构成的循环依赖是没法解决的,由于加入singletonFactories三级缓存的前提是执行了构造器,因此构造器的循环依赖无法解决,只能抛异常BeanCurrentlyInCreationException异常表示循环异常。

Spring是如何解决循环依赖的?

根据上文所述,Spring通过setter方式单例注入,是可以解决循环依赖的。关键源码如下:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { // ... /** *一级缓存:单例池 *存放已经初始化的bean——成品 */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256); /** *三级缓存:单例工厂的高速缓存 *存放生成bean的工厂 */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16); /** *二级缓存:早期单例对象的高速缓存 *存放已经实例化但未初始化(未填充属性)的的bean——半成品 */ private final Map<String, Object> earlySingletonObjects = new HashMap(16); //... /** * *此处就是解决循环依赖的关键,这段代码发生在createBeanInstance以后, *也就是说单例对象此时已经被建立出来的。这个对象已经被生产出来了, *虽然还不完美(尚未进行初始化的第二步和第三步), *可是已经能被人认出来了(根据对象引用能定位到堆中的对象), *因此Spring此时将这个对象提早曝光出来让你们认识,让你们使用。 */ protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized(this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } } // ... @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { //首先去一级缓存中获取如果获取的到说明bean已经存在,直接返回 Object singletonObject = this.singletonObjects.get(beanName); //如果一级缓存中不存在,则去判断该bean是否在创建中,如果该bean正在创建中,就说明了,这个时候发生了循环依赖 if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { synchronized(this.singletonObjects) { //如果发生循环依赖,首先去二级缓存中获取,如果获取到则返回,这个地方就是获取aop增强以后的bean singletonObject = this.earlySingletonObjects.get(beanName); //如果二级缓存中不存在,且允许提前访问三级引用 if (singletonObject == null && allowEarlyReference) { //去三级缓存中获取 ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName); if (singletonFactory != null) { //如果三级缓存中的lambda表达式存在,执行aop,获取增强以后的对象,为了防止重复aop,将三级缓存删除,升级到二级缓存中 singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; } // ...}

以上便是spring解决循环依赖的核心代码,那么对于:A对象setter依赖B对象,B对象setter依赖A对象,Spring解决循环依赖的详细步骤如下:

  • (1) A首先完成了初始化的第一步,而且将本身提早曝光到singletonFactories中。
  • (2) 此时进行初始化的第二步,发现本身依赖对象B,此时就尝试去get(B),发现B尚未被create,因此走create流程,B在初始化第一步的时候发现本身依赖了对象A,因而尝试get(A),尝试一级缓存singletonObjects(确定没有,由于A还没初始化彻底),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,因为A经过ObjectFactory将本身提早曝光了,因此B可以经过ObjectFactory.getObject拿到A对象(虽然A尚未初始化彻底,可是总比没有好呀),B拿到A对象后顺利完成了初始化阶段一、二。彻底初始化以后将本身放入到一级缓存singletonObjects中。
  • (3) 此时返回A中,A此时能拿到B的对象顺利完成本身的初始化阶段二、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,并且更加幸运的是,因为B拿到了A的对象引用,因此B如今hold住的A对象完成了初始化。

搜索更多有关“spring怎么发现循环依赖?是如何解决循环依赖的”的信息 [百度搜索] [SoGou搜索] [头条搜索] [360搜索]
本网站部分内容、图文来自于网络,如有侵犯您的合法权益,请及时与我们联系,我们将第一时间安排核实及删除!
CopyRight © 2008-2024 蜗牛素材网 All Rights Reserved. 手机版