美文网首页
Spring 是如何解决循环依赖的?你真的能说清楚吗?

Spring 是如何解决循环依赖的?你真的能说清楚吗?

作者: 爪哇驿站 | 来源:发表于2021-12-23 14:02 被阅读0次

    前言

    在面试的时候这两年有一个非常高频的关于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
    @ComponentScan
    public 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@1ed6388a
    com.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 是如何解决循环依赖的?你真的能说清楚吗?

          本文链接:https://www.haomeiwen.com/subject/ohmhqrtx.html