• Spring 循环依赖解决


    主题

    记录一下spring如何解决循环依赖的问题

    首先推荐一下几篇文章

    https://www.iflym.com/index.php/code/201208280001.html

    这里有3篇文章..写的挺不错的.但是还有略有难懂.我想最简单易懂的记录下我的理解.

    3个MAP

    BeanFactory中有3个map

    /** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /** Cache of singleton factories: bean name to ObjectFactory. */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    /** Cache of early singleton objects: bean name to bean instance. */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    第一个map singletonObjects就是最终结果.BF加工完成的bean都会被丢到这里(同时情况其他2个map)

    第二个map singletonFactories是一个中间过程map. 当一个bean被new,但是还没有populate他的属性,就是说还没加工完成,属性都没设置,初始化方法(init-method,afterProperties,或者BeanPostProcessor还没处理它的时候).这个bean会被丢到这个map里.也就是说,每个bean首先会被加到这个map里.当然完全初始化完成的时候就会被remove,丢到第一个map里.

    第三个map earlySingletonObjects是专门用于处理循环依赖的.因为A,B循环依赖的时候,需要把A提早暴露出来set到B的属性里.所以这个map的名字也挺能看出作用的.earlySingleton..那意思就是提早暴露的单例.当A引用B再引用A的时候就会把A从singletonFactories中remove,丢到earlySingletonObjects中.(完全初始化完成以后还是会被丢到第一个map里,所以这里也相当于一个中间状态)

    流程

    首先有BF中3个重要方法会涉及到这3个map..

     1  /**
     2      * Return the (raw) singleton object registered under the given name.
     3      * <p>Checks already instantiated singletons and also allows for an early
     4      * reference to a currently created singleton (resolving a circular reference).
     5      * @param beanName the name of the bean to look for
     6      * @param allowEarlyReference whether early references should be created or not
     7      * @return the registered singleton object, or <code>null</code> if none found
     8      */
     9     protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    10         Object singletonObject = this.singletonObjects.get(beanName);
    11         if (singletonObject == null) {
    12             synchronized (this.singletonObjects) {
    13                 singletonObject = this.earlySingletonObjects.get(beanName);
    14                 if (singletonObject == null && allowEarlyReference) {
    15                     ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
    16                     if (singletonFactory != null) {
    17                         singletonObject = singletonFactory.getObject();
    18                         this.earlySingletonObjects.put(beanName, singletonObject);
    19                         this.singletonFactories.remove(beanName);
    20                     }
    21                 }
    22             }
    23         }
    24         return (singletonObject != NULL_OBJECT ? singletonObject : null);
    25     }
    26 
    27 
    28 
    29 
    30 
    31 /**  
    32 * Add the given singleton factory for building the specified singleton
    33      * if necessary.
    34      * <p>To be called for eager registration of singletons, e.g. to be able to
    35      * resolve circular references.
    36      * @param beanName the name of the bean
    37      * @param singletonFactory the factory for the singleton object
    38      */
    39     protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory) {
    40         Assert.notNull(singletonFactory, "Singleton factory must not be null");
    41         synchronized (this.singletonObjects) {
    42             if (!this.singletonObjects.containsKey(beanName)) {
    43                 this.singletonFactories.put(beanName, singletonFactory);
    44                 this.earlySingletonObjects.remove(beanName);
    45                 this.registeredSingletons.add(beanName);
    46             }
    47         }
    48     }
    49 
    50 
    51 
    52 
    53 
    54 
    55 
    56 
    57 
    58 
    59 
    60 
    61 /**
    62      * Add the given singleton object to the singleton cache of this factory.
    63      * <p>To be called for eager registration of singletons.
    64      * @param beanName the name of the bean
    65      * @param singletonObject the singleton object
    66      */
    67     protected void addSingleton(String beanName, Object singletonObject) {
    68         synchronized (this.singletonObjects) {
    69             this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
    70             this.singletonFactories.remove(beanName);
    71             this.earlySingletonObjects.remove(beanName);
    72             this.registeredSingletons.add(beanName);
    73         }
    74     }

    然后我们来按首先加载bean A的顺序来屡一下过程, 假设配置是这样的

     首先假设BF要加载bean A(先加载B也可以...原理一样..这里假设先加载A)

    BF就会掉getSingleton,这个时候allowEarlyReference是true.但是很可惜,3个map都是空的,任何bean都没有..所以啥都没,返回是null.

    然后就会去创建A

    L588: 这个时候A已经new了.但是还没有到L594的初始化属性.这个时候就会调用addSingletonFactory.这个时候Bean A就会被丢到singletonFactories中去..

    然后到了L594这个时候因为A类里有类B的对象需要注入.所以BF会去doGetBean B.然后流程和之前创建A一样..做到populateBean B之前.这个时候singletonFactories中有A和B 2个对象.

    然后到了L594去populate B的属性..BF又会去doGetBean A.

    这个时候就和第一次创建A的时候有区别了.因为A已经在singletonFactories中了,所以getSingleton方法中会把A从singletonFactories remove掉,丢到earlySingletonObjects中去.同时返回A这个对象.

    然后B拿到了还没初始化完成(属性还没设置)的A设置到自己的属性中去.并完成自己的populate..然后B完全初始化以后.会调用ddSingleton方法.把Bean B从singletonFactories中remove掉,丢到singletonObjects中去,标志着B完成初始化,可以被使用了.

    B ok了以后,回到A的populate方法.因为属性都已经populate了.所以A也初始化完成了.和B的结束过程一样.也需要丢到singletonObjects中去.只是是从earlySingletonObjects中remove,而不是像B一样从singletonFactories中remove.

    至此A,B 2个对象都加工完成.

    问题

    这么操作会不会有什么问题?

    会有的.

    1.循环依赖需要单例的bean是通过非构造方法注入的.因为需要在earlySingletonObjects中缓存new了但是还没加工完成bean.

    如果需要通过构造方法注入,那Bean就还没new完..所以会出现A需要等B先new.而B需要等A先new.那就无限循环了.

    2.A,B不可以被wrap..我们知道BeanPostProcessor等特殊的bean可以替换掉原始的bean.返回其他bean替换掉原本的bean.比如postProcessBeforeInitialization方法.

      

    假设我有个BPP对A处理,返回新的bean.如上图

    那就会出现B中的A是个原始没被代理(还没经过postProcessBeforeInitialization方法处理)的bean..但是最终BF中的加工完成的A又是经过代理的Bean.这样就会有问题.

    spring会检测并报错

    exposedObject = initializeBean(beanName, exposedObject, mbd); 这一行中

    经过initializeBean处理的bean叫做exposedObject, 它和原本populateBean方法中set到B中的A这个bean(方法里就是bean这个变量名)可能会不一致.如果出现这种情况

    就会进入else if线路..检测出A前后变化了..而B中却有原始的A..并且B已经加工完成...对于这样的情况.就会throw BeanCurrentlyInCreationException这个异常..

  • 相关阅读:
    每天一个Linux命令(26)chown命令
    每天一个Linux命令(25)chgrp命令
    每天一个Linux命令(24)tar命令
    每天一个Linux命令(23)chmod命令
    每天一个Linux命令(22)find命令_命令详解
    数据结构与算法分析(3)算法分析
    每天一个Linux命令(21)find命令_xargs参数
    每天一个Linux命令(20)find命令_exec参数
    每天一个Linux命令(19)find命令_初识
    Hadoop ->> 关于数据分割(Data Split)的思考
  • 原文地址:https://www.cnblogs.com/abcwt112/p/12577109.html
Copyright © 2020-2023  润新知