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这个异常..

 

posted @ 2020-03-26 20:25  abcwt112  阅读(447)  评论(0编辑  收藏  举报