菜瓜: 水稻, 这次我特意去看了 java 的循环依赖
水稻: 哟, 有什么收获
菜瓜: 两种情况, 构造器循环依赖, 属性循环依赖
构造器循环依赖在逻辑层面无法通过. 对象通过构造函数创建时如果需要创建另一个对象, 就会存在递归调用. 栈内存直接溢出
属性循环依赖可以解决. 在对象创建完成之后通过属性赋值操作.
- package club.interview.base;
- /**
- * 构造器循环依赖 - Exception in thread "main" java.lang.StackOverflowError
- * toString()循环打印也会异常 - Exception in thread "main" java.lang.StackOverflowError
- * @author QuCheng on 2020/6/18.
- */
- public class Circular {
- class A {
- B b;
- // public A() {
- // b = new B();
- // }
- // @Override
- // public String toString() {
- // return "A{" +
- // "b=" + b +
- // '}';
- // }
- }
- class B {
- A a;
- // public B() {
- // a = new A();
- // }
- // @Override
- // public String toString() {
- // return "B{" +
- // "a=" + a +
- // '}';
- // }
- }
- private void test() {
- B b = new B();
- A a = new A();
- a.b = b;
- b.a = a;
- System.out.println(a);
- System.out.println(b);
- }
- public static void main(String[] args) {
- new Circular().test();
- }
- }
水稻: 厉害啊, Spring 也不支持构造函数的依赖注入, 而且也不支持多例的循环依赖. 同样的, 它支持属性的依赖注入.
看效果 - 如果 toString()打印同样会出现栈内存溢出.
- package com.vip.qc.circular;
- import org.springframework.stereotype.Component;
- import javax.annotation.Resource;
- /**
- * @author QuCheng on 2020/6/18.
- */
- @Component("a")
- public class CircularA {
- @Resource
- private CircularB circularB;
- // @Override
- // public String toString() {
- // return "CircularA{" +
- // "circularB=" + circularB +
- // '}';
- // }
- }
- package com.vip.qc.circular;
- import org.springframework.stereotype.Component;
- import javax.annotation.Resource;
- /**
- * @author QuCheng on 2020/6/18.
- */
- @Component("b")
- public class CircularB {
- @Resource
- private CircularA circularA;
- // @Override
- // public String toString() {
- // return "CircularB{" +
- // "circularA=" + circularA +
- // '}';
- // }
- }
- @Test
- public void testCircular() {
- String basePackages = "com.vip.qc.circular";
- new AnnotationConfigApplicationContext(basePackages);
- }
菜瓜: 看来 spring 的实现应该也是通过属性注入的吧
水稻: 你说的对. 先给思路和 demo, 之后带你扫一遍源码, follow me !
spring 的思路是给已经初始化的 bean 标记状态, 假设 A 依赖 B,B 依赖 A, 先创建 A
先从缓存容器 (总共三层, 一级拿不到就拿二级, 二级拿不到就从三级缓存中拿正在创建的) 中获取 A, 未获取到就执行创建逻辑
对象 A 在创建完成还未将属性渲染完之前标记为正在创建中, 放入三级缓存容器. 渲染属性 populateBean()会获取依赖的对象 B.
此时 B 会走一次 getBean 逻辑, B 同样会先放入三级缓存, 然后渲染属性, 再次走 getBean 逻辑注入 A, 此时能从三级缓存中拿到 A, 并将 A 放入二级容器. B 渲染完成放入一级容器
回到 A 渲染 B 的方法 populateBean(), 拿到 B 之后能顺利执行完自己的创建过程. 放入一级缓存
为了证实结果, 我把源码给改了一下, 看结果
- package com.vip.qc.circular;
- import org.springframework.stereotype.Component;
- import javax.annotation.Resource;
- /**
- * @author QuCheng on 2020/6/18.
- */
- @Component("a")
- public class CircularA {
- @Resource
- private CircularB circularB;
- @Override
- public String toString() {
- return "CircularA{" +
- "circularB=" + circularB +
- '}';
- }
- }
- package com.vip.qc.circular;
- import org.springframework.stereotype.Component;
- import javax.annotation.Resource;
- /**
- * @author QuCheng on 2020/6/18.
- */
- @Component("b")
- public class CircularB {
- @Resource
- private CircularA circularA;
- @Override
- public String toString() {
- return "CircularB{" +
- "circularA=" + circularA +
- '}';
- }
- }
测试代码
- @Test
- public void testCircular() {
- String basePackages = "com.vip.qc.circular";
- new AnnotationConfigApplicationContext(basePackages);
- }
测试结果(我改过源码了)
----
将 a 放入三级缓存
将 b 放入三级缓存
将 a 放入二级缓存
将 b 放入一级缓存
从二级缓存中拿到了 a
将 a 放入一级缓存
再看源码
关键类处理 getSingleton 逻辑 - 缓存容器
- public class DefaultSingletonBeanRegistry
- /** 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);
主流程 AbstractApplicationContext#refresh() -> DefaultListableBeanFactory#preInstantiateSingletons() -> AbstractBeanFactory#getBean() & #doGetBean()
- protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
- @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
- /**
- * 处理 FactoryBean 接口名称转换 {@link BeanFactory#FACTORY_BEAN_PREFIX }
- */
- final String beanName = transformedBeanName(name);
- ...
- // 1从缓存中拿对象(如果对象正在创建中且被依赖注入, 会放入二级缓存)
- Object sharedInstance = getSingleton(beanName);
- if (sharedInstance != null && args == null) {
- ...
- }else {
- ...
- if (mbd.isSingleton()) {
- // 2 将创建的对象放入一级缓存
- sharedInstance = getSingleton(beanName, () -> {
- try {
- // 3 具体创建的过程, 每个 bean 创建完成之后都会放入三级缓存, 然后渲染属性
- return createBean(beanName, mbd, args);
- }catch (BeansException ex) {
- ...
- ...
- return (T) bean;
- }
1getSingleton(beanName) - 二级缓存操作
- protected Object getSingleton(String beanName, boolean allowEarlyReference) {
- // 实例化已经完成了的放在 singletonObjects
- Object singletonObject = this.singletonObjects.get(beanName);
- // 解决循环依赖
- 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);
- if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
- System.out.println("将"+beanName+"放入二级缓存");;
- this.singletonFactories.remove(beanName);
- }
- }else if(singletonObject != null){
- System.out.println("从二级缓存中拿到了"+beanName);
- }
- }
- }
- return singletonObject;
- }
2 getSingleton(beanName,lamdba) - 一级缓存操作
- public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
- Assert.notNull(beanName, "Bean name must not be null");
- synchronized (this.singletonObjects) {
- Object singletonObject = this.singletonObjects.get(beanName);
- if (singletonObject == null) {
- if (this.singletonsCurrentlyInDestruction) {
- ...
- // 正在创建的 bean 加入 singletonsCurrentlyInCreation - 保证只有一个对象创建, 阻断循环依赖
- beforeSingletonCreation(beanName);
- ...
- try {
- singletonObject = singletonFactory.getObject();
- ...
- finally {
- ...
- // 从 singletonsCurrentlyInCreation 中移除
- afterSingletonCreation(beanName);
- }
- if (newSingleton) {
- // 对象创建完毕 - 放入一级缓存(从其他缓存移除)
- addSingleton(beanName, singletonObject);
- }
- }
- return singletonObject;
- }
- }
- // ----- 内部调用一级缓存操作
- protected void addSingleton(String beanName, Object singletonObject) {
- synchronized (this.singletonObjects) {
- if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
- System.out.println("将"+beanName+"放入一级缓存");;
- this.singletonObjects.put(beanName, singletonObject);
- this.singletonFactories.remove(beanName);
- this.earlySingletonObjects.remove(beanName);
- this.registeredSingletons.add(beanName);
- }
- }
3createBean(beanName, mbd, args) - 三级缓存操作
- protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {
- ...
- if (instanceWrapper == null) {
- // 5* 实例化对象本身
- instanceWrapper = createBeanInstance(beanName, mbd, args);
- }
- ...
- boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
- isSingletonCurrentlyInCreation(beanName));
- if (earlySingletonExposure) {
- ...
- // 将创建好还未渲染属性的 bean 放入三级缓存
- addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
- }
- Object exposedObject = bean;
- try {
- // 渲染 bean 自身和属性
- populateBean(beanName, mbd, instanceWrapper);
- // 实例化之后的后置处理 - init
- exposedObject = initializeBean(beanName, exposedObject, mbd);
- }
- catch (Throwable ex) {
- ...
- return exposedObject;
- }
- // ------------- 内部调用三级缓存操作
- protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
- Assert.notNull(singletonFactory, "Singleton factory must not be null");
- synchronized (this.singletonObjects) {
- if (!this.singletonObjects.containsKey(beanName)) {
- if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
- System.out.println("将"+beanName+"放入三级缓存");;
- this.singletonFactories.put(beanName, singletonFactory);
- this.earlySingletonObjects.remove(beanName);
- this.registeredSingletons.add(beanName);
- }
- }
- }
菜瓜: demo 比较简单, 流程大致明白, 源码我还需要斟酌一下, 整体有了概念. 这个流程好像是掺杂在 bean 的创建过程中, 结合 bean 的生命周期整体理解可能会更深入一点
水稻: 是的. 每个知识点都不是单一的, 拿着 bean 的生命周期再理解一遍可能会更有收获.
讨论
为什么是三级缓存, 两级不行吗?
猜测: 理论上两级也可以实现. 多一个二级缓存可能是为了加快获取的速度. 加入 A 依赖 B,A 依赖 C,B 依赖 A,C 依赖 A, 那么 C 在获取 A 的时候只需要从二级缓存中就能拿到 A 了
总结
Spring 的处理方式和 java 处理的思想一致, 构造器依赖本身是破坏语义和规范的
属性赋值 --> 依赖注入 . 先创建对象, 再赋值属性, 赋值的时候发现需要创建便生成依赖对象, 被依赖对象需要前一个对象就从缓存容器中拿取即可
来源: https://www.cnblogs.com/nightOfStreet/p/13160115.html