zoukankan      html  css  js  c++  java
  • Spring循环依赖

            什么是循环依赖?2个或以上bean 互相持有对方,最终形成闭环。

            

           

            Spring容器怎么解决循环依赖的问题?之前看了一些相关文章,感觉还是一头雾水,最后还是在Spring源码中找到了答案,下面就来看下Spring解决循环依赖的大致流程。

    DefaultSingletonBeanRegistry:单例bean的注册容器 ,AbstractAutowireCapableBeanFactory工厂类本身继承了DefaultSingletonBeanRegistry

    /** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    
    /** Set of registered singletons, containing the bean names in registration order */
    private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

    三级缓存

           singletonObjects:单例对象的cache

           singletonFactories : 单例对象工厂的cache 

           earlySingletonObjects :提前曝光的单例对象的Cache 。[用于检测循环引用,与singletonFactories互斥]

    getSinleton:获取单例bean的方法

    @Nullable
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
         //singletonObjects中获取bean,如果为null,并且这个单例实例正在creating中
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                //暴露单例Map中获取bean,如果为空,并且allowEarlyReference允许提前暴露bean的引用
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        //根据beanName从singletonFactories获取singletonFactories不为空
                        singletonObject = singletonFactory.getObject();
                        //提前暴露bean的引用
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        //singletonFactories中移除beanName
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

           Spring对于循环引用的解决方案就从缓存中取到正在创建的bean的引用,此处的bean必须是单例的

          上面代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。

     

           假如“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。

           为啥Spring不能解决“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象”这类问题了!因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

           AbstractAutowireCapableBeanFactory类中 doCreateBean部分代码

            // Eagerly cache singletons to be able to resolve circular references
            // even when triggered by lifecycle interfaces like BeanFactoryAware.
            //向容器中缓存单例模式的Bean对象,以防循环引用
            boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                    isSingletonCurrentlyInCreation(beanName));
            if (earlySingletonExposure) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Eagerly caching bean '" + beanName +
                            "' to allow for resolving potential circular references");
                }
                //这里是一个匿名内部类,为了防止循环引用,尽早持有对象的引用
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            }
    
        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);
                }
            }
        }

          上面代码发生在createBeanInstance(也在doCreateBean方法中)之后,也就是说单例对象此时已经被创建出来(调用了构造器),这个对象已经被生产出来了,虽然还不是我们最终的对象,而调用构造器的时候bean还没有暴露在singletonFactories三级缓存中,会抛出BeanCurrentlyInCreationException异常。

         原型(Prototype)模式下为什么不能循环依赖?

         AbstractBeanFactory :bean工厂

    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
            /*省略部分代码*/
            // Fail if we're already creating this bean instance:
            // We're assumably within a circular reference.
            //如果是原型bean,且bean正在被创建抛异常
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }
            /*省略部分代码*/
  • 相关阅读:
    java 日志体系
    java mail 接收邮件
    Spring 事物Transaction
    Spring 文件上传MultipartFile 执行流程分析
    centos7安装Elasticsearch7
    centos7安装docker笔记
    docker安装
    redis
    springboot+redis+nginx+分布式session
    tomcat程序和webapp分离
  • 原文地址:https://www.cnblogs.com/dyg0826/p/11200980.html
Copyright © 2011-2022 走看看