简介

说明

        本文用实例来介绍@Autowired解决循环依赖的原理。@Autowired是通过三级缓存来解决循环依赖的。 

        除了@Autoired,还有其他方案来解决循环依赖的​

概述

        @Autowired进行属性注入可以解决循环依赖。因为按Java的类加载流程来说,是先实例化,后注入属性的。Spring中记录了正在创建中的bean(已经实例化但还没初始化完毕的bean),所以可以在之后注入属性时,从记录的bean中取依赖的对象。

        相对而言,单纯使用构造器注入就无法解决循环依赖。因为,在构造时就需要传入依赖的对象,导致无法实例化。(注意:构造器注入可以使用@Lazy解决循环依赖,在实例化时,传入代理对象,真正使用时才会生成真正的对象)

实例

代码

package com.example.tmp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class A {
@Autowired
private B b;

private String name = "Tony";

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getTest() {
return b.getAge().toString() + name;
}
}
package com.example.tmp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class B {
@Autowired
private A a;

private Integer age = 20;

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}
}
package com.example.controller;

import com.example.tmp.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
@Autowired
private A a;

@GetMapping("/test1")
public String test1() {
return a.getTest();
}
}

测试

1.启动不报错。

2.postman访问:​​http://localhost:8080/test1​

后端结果:不报错

postman结果: 20Tony

打断点分析​

简介

由上边两篇博客,可以定位到实例化的代码位置:

AbstractAutowireCapableBeanFactory#doCreateBean(beanName, mbdToUse, args)

1.总入口:populateBean

总结

        经过下边的定位,可确定,核心代码在:AbstractAutowireCapableBeanFactory#populateBean(beanName, mbd, instanceWrapper);

        doGetBean 中有两个 getSingleton 方法会先后执行。第一个是尝试从缓存中获取,若缓存中没有 A,则执行第二个,通过工厂获得。

public Object getSingleton(String beanName) 。

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)。

        第一次通过getSingleton(String beanName)获取bean的时候,缓存中是没有的,于是走到getSingleton(String beanName, ObjectFactory<?> singletonFactory)

流程概述

getBean(beanName) //AbstractBeanFactory抽象类(实现的BeanFactory接口)

    doGetBean(name, null, null, false) //AbstractBeanFactory抽象类。自己的方法。

        // 先从单例的缓存中取,取不到则走下一个getSingleton

        getSingleton(beanName);

        getSingleton(beanName, () -> {

                        try {

                            return createBean(beanName, mbd, args);

                        }}

                     )


createBean(beanName, mbd, args) //AbstractAutowireCapableBeanFactory抽象类(实现的AbstractBeanFactory)

    doCreateBean(beanName, mbdToUse, args)//AbstractAutowireCapableBeanFactory抽象类。以下为此类方法

        createBeanInstance(beanName, mbd, args);  //创建实例

        applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName) //post-processors修改bean的定义

        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); //解决循环依赖

        populateBean(beanName, mbd, instanceWrapper); //填充属性 

断点调试

打条件断点(DefaultListableBeanFactory#doCreateBean(beanName, mbdToUse, args)

Spring(SpringBoot)--循环依赖的原理--@Autowired_三级缓存

定位代码:实例化A时去实例化B并填充到A对象


在populateBean前后打断点。

结果:


  1. populateBean前(A的):A已经实例化,其字段b还是null。(图1)
  2. populateBean后(A的):没走到下一行,而是去实例化B去了,再次到了populateBean前。(图2)
  1. 此时,B已经实例化,其字段a为null
  1. populateBean后(B的):的对象里已经给字段a赋值了,但a的字段b仍然为null。(图3)
  2. populateBean后(A的):在第3步执行完之后,堆栈执行到A的populateBean后边。(图4)
  1. 此时,A对象里有B对象,B对象里边有A对象,一直循环

 图1:

Spring(SpringBoot)--循环依赖的原理--@Autowired_循环依赖_02

图2: 

Spring(SpringBoot)--循环依赖的原理--@Autowired_三级缓存_03

图3:

Spring(SpringBoot)--循环依赖的原理--@Autowired_SpringBoot_04

图4: 

Spring(SpringBoot)--循环依赖的原理--@Autowired_原理_05


2.后置处理器注入属性

简介

经过上边的定位,可以确定,核心代码在:AbstractAutowireCapableBeanFactory#populateBean(beanName, mbd, instanceWrapper);

打条件断点:

Spring(SpringBoot)--循环依赖的原理--@Autowired_循环依赖_06

总结(代码流程)

getBeanPostProcessors() // 其中有:AutowiredAnnotationBeanPostProcessor (图1)

    ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);

        // AutowiredAnnotationBeanPostProcessor

        postProcessProperties(PropertyValues pvs, Object bean, String beanName)  (图2)

            InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);

            metadata.inject(bean, beanName, pvs);

                inject(bean, beanName, pvs) //InjectionMetadata

                    element.inject(target, beanName, pvs); //InjectionMetadata      (图3)

                        // AutowiredAnnotationBeanPostProcessor

                        // 第一次进来时,this.cached为false。去解决依赖(B对象)(图4)

                        value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter)

                        // 上边获得之后,使用反射,设置字段的值

                        field.set(bean, value);

图1: 

Spring(SpringBoot)--循环依赖的原理--@Autowired_原理_07

图2: 

Spring(SpringBoot)--循环依赖的原理--@Autowired_SpringBoot_08

图3: 

Spring(SpringBoot)--循环依赖的原理--@Autowired_原理_09

图4:

Spring(SpringBoot)--循环依赖的原理--@Autowired_循环依赖_10

3.解决依赖

总结

        实例化B时,由于之前实例化过A,这次 getSingleton(String beanName) 中发生了不一样的事:能够获得 A 的缓存。

简介

        从上一步可以看到,最终调用DefaultListableBeanFactory#resolveDependency,来解决 A => B 的依赖,需要去获取 B,仍然通过 getBean 获取,和之前说 getBean 获取 A 的过程类似,只是这次换成了 B。

        调用栈如下:getBean=> doGetBean=> getSingleton,又是熟悉的步骤,但这次 getSingleton(String beanName) 中发生了不一样的事:能够获得 A 的缓存。

Spring(SpringBoot)--循环依赖的原理--@Autowired_原理_11

4.三级缓存

总结

可以在第3级缓存中获得到bean。

简介


在AbstractBeanFactory#doGetBean()上打两个条件断点:beanName.equals("a") || beanName.equals("b") 

// 为什么不在getSingleton里边打断点?因为getSingleton除了获取bean之外,其他也有地方调用了,会影响我们本处的分析。

断点1

Spring(SpringBoot)--循环依赖的原理--@Autowired_三级缓存_12

断点2: 

Spring(SpringBoot)--循环依赖的原理--@Autowired_三级缓存_13


第1次到达断点1:Controller对象获取A对象,三级缓存中都没有

Spring(SpringBoot)--循环依赖的原理--@Autowired_Autowired_14

第1次到达断点2:缓存中都没有对象A,所以去创建

Spring(SpringBoot)--循环依赖的原理--@Autowired_原理_15

第2次到达断点1: 因为A类依赖了B类,所以获取B对象。但也是:三级缓存中都没有B对象

Spring(SpringBoot)--循环依赖的原理--@Autowired_循环依赖_16

第2次到达断点2: 缓存中都没有对象B,所以去创建

Spring(SpringBoot)--循环依赖的原理--@Autowired_Autowired_17

第3次到达断点1: 因为B类依赖了A类,所以获取A对象。此时:第1级缓存中已经有A对象了

Spring(SpringBoot)--循环依赖的原理--@Autowired_原理_18

第4次到达断点1:A用@Component修饰,要扫描进来。此时缓存中已经有了

Spring(SpringBoot)--循环依赖的原理--@Autowired_循环依赖_19

第5次到达断点1:B用@Component修饰,要扫描进来。此时缓存中已经有了

Spring(SpringBoot)--循环依赖的原理--@Autowired_原理_20

三级缓存

概述

三级缓存流程如下:


  1. 从一级缓存singletonObjects中获取。()
  2. 若一级缓存中获取不到,则从二级缓存earlySingletonObjects中获取
  3. 若二级缓存中获取不到,则从三级缓存singletonFactories中获取
  1. 通过beanName获得对应的ObjectFactory,若能够获取到,则:

  1. 调用ObjectFactory#getObject获得真正的bean
  2. 把此bean从三级缓存移到二级缓存。即:singletonFactories中移除,并放入earlySingletonObjects中。


DefaultSingletonBeanRegistry 里边的三级缓存对应的map,如下所示:

/** Cache of singleton objects: bean name to bean instance. */
// 缓存单例Bean。key:bean名字,value:bean实例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of early singleton objects: bean name to bean instance. */
// 缓存半成品单例Bean。key:bean名字,value:bean实例(已实例化,但未注入属性和初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** Cache of singleton factories: bean name to ObjectFactory. */
// 缓存单例bean的工厂。key:bean名字,value:能够生成bean的工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

代码位置

从上边可以追踪到从缓存中获取bean的位置:DefaultSingletonBeanRegistry#getSingleton(String beanName, boolean allowEarlyReference)

protected Object getSingleton(String beanName, boolean allowEarlyReference) {

    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);

                    this.singletonFactories.remove(beanName);

                }

            }

        }

    }

    return singletonObject;

}

bean何时被加入第3级缓存?

简介

在上边“4.三级缓存”中可以看到,第三级缓存(singletonFactories)里边放入了以bean名为键,ObjectFactory为值的项。那么,是何时放入的呢?

答:在AbstractAutowireCapableBeanFactory#doCreateBean中,populateBean之前放入的,方法为:addSingletonFactory

doCreateBean //AbstractAutowireCapableBeanFactory


// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    if (logger.isTraceEnabled()) {
        logger.trace("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
    }
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

// Initialize the bean instance.
Object exposedObject = bean;
try {
    populateBean(beanName, mbd, instanceWrapper);
    exposedObject = initializeBean(beanName, exposedObject, mbd);
}



addSingletonFactory  //DefaultSingletonBeanRegistry

// 放入以bean名为键,ObjectFactory为值的项

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}



getEarlyBeanReference //AbstractAutowireCapableBeanFactory

// 返回 A 的引用。(虽然 A 还在创建,未完成。)

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            }
        }
    }
    return exposedObject;
}


三级缓存流程图

流程图

Spring(SpringBoot)--循环依赖的原理--@Autowired_Autowired_21

总结


  • 对于不同的bean,获取某个循环依赖的对象的位置是不同的。
  • 对于第一次获取:缓存中没有,会去创建它,然后获取。
  • 对于第二次获取:第3级缓存中有,会从第3级缓存中获取,然后将其转移到第2级缓存

getSingleton(String beanName, ObjectFactory<?> singletonFactory)


代码流程:

入口

getSingleton(String beanName, ObjectFactory<?> singletonFactory) //DefaultSingletonBeanRegistry
    // DefaultSingletonBeanRegistry
    singletonObject = singletonFactory.getObject();
    addSingleton(beanName, singletonObject);

addSingleton(String beanName, Object singletonObject)   // DefaultSingletonBeanRegistry

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}


为什么用三级缓存,而不是二级缓存

简介

在上边的分析中我们可以提出两个问题:


  1. 二级缓存好像没有用到?那么它什么时候会用到?
  2. 为什么第三级缓存要用一个工厂,删除第三级缓存,只用第一二级不可以吗?

可以去掉第二级缓存吗?

简介

不可以去掉第二级缓存。

详解

假如有这种情况:a实例同时依赖于b和c,b和c都依赖于a。

        a实例化时,先提前暴露objectFactorya到三级缓存,调用getBean(b)依赖注入b实例。b实例化之后,提前暴露objectFactoryb到三级缓存,调用getBean(a)依赖注入a实例,由于提前暴露了objectFactorya,此时可以从三级缓存中获取到a实例, b实例完成了依赖注入,升级为一级缓存。a实例化再getBean(c)依赖注入c实例,c实例化之后,提前暴露objectFactoryc到三级缓存,调用getBean(a)依赖注入a实例,由于提前暴露了objectFactorya,此时可以从三级缓存中获取到a实例。注意这里又要从三级缓存中获取a实例,我们知道三级缓存中的实例是通过调用singletonFactory.getObject()方法获取的,返回结果每次都可能不一样。如果不用二级缓存,这里会有问题,两次获取的a实例不一样。

可以去掉第三级缓存吗?

结论

不可以去掉第三级缓存。

原因

Spring 的设计原则是在 IOC 结束之后再AOP( bean 实例化、属性设置、初始化之后再通过进行AOP(生成代理对象))。即:AOP的实现需要与bean的生命周期的创建分离。


  1. 为了解决循环依赖但又尽量不打破这个设计原则的情况下,使用了第三级缓存(key:bean名字,value:ObjectFactory)。
  1. 前边分析过,它是将一个函数式接口作为ObjectFactory,相当于延迟初始化。AOP中发生循环依赖时,通过调用Object的getObject()方法获取到三级缓存中的对象。
  1. 如果去掉第三级缓存,将AOP的代理工作放到第二级缓存,这样的话,bean在创建过程中就先生成代理对象再初始化和其他工作,与Spring的AOP的设计原则相违背。

详解


        上边:“bean何时被加入第3级缓存?”  我们可以得到,第三级缓存里存放的value是:AbstractAutowireCapableBeanFactory#getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean)。

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {     Object exposedObject = bean;     if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {         for (BeanPostProcessor bp : getBeanPostProcessors()) {             if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {                 SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;                 exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);             }         }     }     return exposedObject; }

可以看到实际调用SmartInstantiationAwareBeanPostProcessor接口的getEarlyBeanReference(Object bean, String beanName)方法,此接口有以下实现类:

Spring(SpringBoot)--循环依赖的原理--@Autowired_SpringBoot_22

InstantiationAwareBeanPostProcessorAdapter

@Override public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { return bean; }

直接返回bean,这里看起来第三级缓存没必要存在。但是,看另一个实现类:

AbstractAutoProxyCreator

@Override public Object getEarlyBeanReference(Object bean, String beanName) { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.put(cacheKey, bean); return wrapIfNecessary(bean, beanName, cacheKey); }

这里把对象放入第二级缓存。​ 曾分析过AOP:将横切逻辑织入目标Bean:AbstractAutoProxyCreator#postProcessAfterInitialization方法,在Bean实例化之后被调用。

来看postProcessAfterInitialization

@Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }

把key对应的项从二级缓存中移除,如果原来二级缓存中就是这个bean,则返回此bean,否则,返回代理类。