循环依赖
A类属性有B类,B类属性有A类,相互依赖。
依赖注入
主要有两种方式1、构造器;2、setter,其中构造器方式产生的循环依赖无解,会抛出异常。
Spring类加载主要过程
实例化->初始化,其中实例化是创建对象,初始化是填充属性。我们把实例化完成的类成为半成品,把初始化完成的类成为成品。
解决循环依赖的关键是将实例化与初始化两个过程分开。
Spring三级缓存
缓存 | map | 存储对象 |
一级缓存 | singletonObjects | 用来存放已经完全创建好的单例bean |
二级缓存 | earlySingletonObjects | 在产生循环依赖的时候,用来存放从三级缓存中获取的半成品bean,若存在代理,其中从三级缓存中最终获取的是aop生成代理对象 |
三级缓存 | singletonFactories | 用来存放单例bean的ObjectFactory |
A、B循环依赖加载过程
1、getSingleton("a", true) 获取a:会依次从 3个级别的缓存中找 a,此时 3个级别的缓存中都没有 a;
2、将a丢到正在创建的 beanName 列表中(singletonsCurrentlyInCreation);
3、实例化 a:A a = new A();这个时候 a 对象是早期的 a,属于半成品;
4、将早期的 a 丢到3级缓存中(singletonFactories);
5、调用 populateBean 方法,注入依赖的对象,发现 setB 需要注入 b;
6、调用getSingleton("b", true)获取 b:会依次从 3 个级别的缓存中找 a,此时 3 个级别的缓存中都没有 b;
7、将 b丢到正在创建的beanName列表中;
8、实例化 b:B b = new B();这个时候 b对象是早期的b,属于半成品;
9、将早期的 b 丢到3级缓存中(singletonFactories);
10、调用populateBean 方法,注入依赖的对象,发现 setA 需要注入 a;
11、调用 getSingleton("a", true) 获取 a:此时 a 会从第 3 级缓存中被移到第 2 级缓存,然后将其返回给 b 使用,此时 a 是个半成品(属性还未填充完毕),此时若a被代理,则会返回代理对象(aop);
12、b 通过 setA 将11中获取的 a 注入到 b中;
13、b 被创建完毕,此时b会从第3级缓存中被移除,然后被丢到1级缓存;
14、b返回给a,然后b被通过 A 类中的 setB 注入给a;
15、a 的populateBean执行完毕,即:完成属性填充,到此时a已经注入到b中了;
总结
1、一级缓存、二级缓存解决循环依赖问题;
2、三级缓存实现aop(代理对象实例化的时候,实例化对象是原始对象,若没有三级缓存,此时若根据类名直接获取对象的话,获取的是原始对象,而我们想要的肯定是通过类名直接获取代理对象,所以Spring在类加载过程中,直接将实例化的对象放入三级缓存中,从三级缓存中获取类对象的时候,判断类是否被代理,若被代理则返回代理对象)