zoukankan      html  css  js  c++  java
  • SpringIOC——DI循环依赖

    要弄清楚循环依赖

    1、需要知道Bean初始化的两个阶段

    ① Bean实例化创建实例对象(new Bean())

    ② Bean实例对象初始化(DI:注解自动注入)

    2、DefaultSingletonBeanRegistry类中的5个容器(补充:很多地方说的三级缓存就是下面的 1/2/3容器)

        /** 记录已将创建的单例<beanName,singletonBean> */
        private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
        /** 记录singletonFactory<beanName,singletonFactory> singeletonFactory中存放beanName和上面的①阶段的bean:Bean实例化实例对象(还未初始化DI)*/
        private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    
        /** 记录早期的singletonBean 存放的也是① */
        private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
    
        /** 存放已经初始化后的beanName,有序 */
        private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
    
        /** 记录正在初始化的bean的beanName */
        private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    其中跟循环依赖相关的是singletonFactories、singeletonsCurrentlyInCreation、earlysingletonObjects.

    3、循环依赖实现

    ①bean初始化前后会打标,加入到singletonsCurrentlyInCreation容器中,这个打标会在核心方法getSingleton()中起作用

    /* org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>) */
        public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
            ...
            //循环依赖相关:初始化前先singletonsCurrentlyInCreation.add(beanName)
            beforeSingletonCreation(beanName);
    ...
    //lamda表达式:其实是调用createBean(beanName, mbd, args):Bean初始化 singletonObject = singletonFactory.getObject(); ... //循环依赖相关:初始化后singletonsCurrentlyInCreation.remove(beanName) afterSingletonCreation(beanName); ...//初始化完后 //this.singletonObjects.put(beanName, singletonObject);放入到单例容器中 //this.singletonFactories.remove(beanName);清空循环依赖的两个打标 //this.earlySingletonObjects.remove(beanName); //this.registeredSingletons.add(beanName);放入单例beanName容器中 addSingleton(beanName, singletonObject); ... } }

    ② 上面singletonObject = singletonFactory.getObject()时向singletonFactories中记录了(new Bean()),singletonFactories也会在核心方法getSingleton()中起作用

    /* org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean */
        protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
                throws BeanCreationException {
            ...
            //循环依赖相关逻辑:
            //this.singletonFactories.put(beanName, singletonFactory);
            //将实例化bean(①阶段)、beanName组装成singletonFactory装入singletonFactories容器
            //this.earlySingletonObjects.remove(beanName);
            //删除earlySingletonObjects中beanName
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
            ...
            //实例初始化  就是在这里面实现依赖注入DI的:反射实现
            //调用AutowiredAnnotationBeanPostProcessor.postProcessProperties
            populateBean(beanName, mbd, instanceWrapper);
            ...
        }

    ③ 核心方法getSingleton

    /* org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) */
        protected Object getSingleton(String beanName, boolean allowEarlyReference) {
            Object singletonObject = this.singletonObjects.get(beanName);
          //循环依赖核心就在于这个判断,由于打标+记录了①阶段的bean,
    //循环依赖第二次调用getBean("a")时,这里会直接返回第一次调用getBean("a")创建的①阶段的bean
    //而不会调用createBean("a")再次bean初始化(造成两个bean的循环创建)
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }

    ④ 循环依赖流程

    /* org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean */
        protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
            ...
            //假设A、B互相依赖
            //第一次getBean(A),sharedInstance == null(一级缓存singletonObjects中没有),bean还未被初始化,走else,createBean初始化bean
            //A打标:正在创建中,代码在①中beforeSingletonCreation()中,,主要逻辑:放入到singetonsCurrentlyInCreation这个Set中
    //A实例化(早期Bean)后保存到singletonFactories中②中addSingletonFactory(beanName,singletonFactory)(即三级缓存)
    //DI依赖注入:②中populateBean(beanName,mbd,instanceWrapper),发现依赖B,调用getBean(B)初始化B的单例
    //调用getBean(B)重复上面步骤,DI依赖注入发现依赖A,调用getBean(A)
    //第二次getBean(A),一级缓存singletonObjects中依然没有初始化完成的Bean,singleObject == null
    //③中if(singletonObject == null && isSingletonCurrentlyInCreation(A))由于打标了所以返回上面创建的早期Bean-A(具体是先访问二级缓存earlySingletonObjects,再访问三级缓存singletonFactories)
    //下面if条件直接返回bean,没有走else破坏了循环
    Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { // bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { ... // createBean(beanName, mbd, args); bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } return bean; }

    四、总结

    未看源码之前,其实对循环依赖有一个想法:循环依赖可以看做是一个死锁。

    预防死锁的方法:打破死锁的四个必要条件(互斥、请求并等待、不可剥夺、循环等待),由于循环依赖的资源是对象自身,所以常用破坏循环等待条件方法:编号顺序执行,不适用

    选择破坏请求并等待条件:先创建对象,再赋值,模型

    A a = new A();
    B b = new B();
    A.b = b;
    B.a = a;

    研究源码之后发现:想法差不多,但是代码实现非常精彩。模型(打标没想到过)

    A a = new A();
    B b = new B();
    b.a = a;
    a.b = b;
  • 相关阅读:
    Azkaban的使用
    Azkaban安装
    Kafka 启动失败,报错Corrupt index found以及org.apache.kafka.common.protocol.types.SchemaException: Error reading field 'version': java.nio.BufferUnderflowException
    Kafka 消费者设置分区策略及原理
    Kafka利用Java API自定义生产者,消费者,拦截器,分区器等组件
    zookeeper群起总是有那么几个节点起不来的问题解决
    flume 启动agent报No appenders could be found for logger的解决
    Flume 的监控方式
    Flume 自定义 组件
    Source r1 has been removed due to an error during configuration java.lang.IllegalArgumentException: Required parameter bind must exist and may not be null & 端口无法连接
  • 原文地址:https://www.cnblogs.com/wqff-biubiu/p/12388072.html
Copyright © 2011-2022 走看看