Spring/SpringBoot--原理--循环依赖--@Autowired

原文网址:Spring/SpringBoot--原理--循环依赖--@Autowired_IT利刃出鞘的博客-CSDN博客

简介

说明

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

        除了@Autoired,还有其他方案来解决循环依赖的,见:Spring/SpringBoot--循环依赖--解决方式/如何解决--实例/示例/实战_IT利刃出鞘的博客-CSDN博客

概述

        @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();
    }
}

测试

启动不报错。

postman访问:http://localhost:8080/test1

后端结果:不报错

postman结果: 20Tony

原理

其他网址

调用到此处代码的流程见:SpringBoot原理--启动流程_IT利刃出鞘的博客-CSDN博客
Bean初始化流程:Bean生命周期--Bean的创建过程_IT利刃出鞘的博客-CSDN博客

简介

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

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_第1张图片

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

在populateBean前后打断点。

结果:

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

 图1:

Spring/SpringBoot--原理--循环依赖--@Autowired_第2张图片

图2: 

Spring/SpringBoot--原理--循环依赖--@Autowired_第3张图片

图3:

Spring/SpringBoot--原理--循环依赖--@Autowired_第4张图片

图4: 

Spring/SpringBoot--原理--循环依赖--@Autowired_第5张图片

2.后置处理器注入属性

简介

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

打条件断点:

Spring/SpringBoot--原理--循环依赖--@Autowired_第6张图片

总结(代码流程)

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_第7张图片

图2: 

Spring/SpringBoot--原理--循环依赖--@Autowired_第8张图片

图3: 

Spring/SpringBoot--原理--循环依赖--@Autowired_第9张图片

图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_第14张图片

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

Spring/SpringBoot--原理--循环依赖--@Autowired_第15张图片

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

Spring/SpringBoot--原理--循环依赖--@Autowired_第16张图片

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

Spring/SpringBoot--原理--循环依赖--@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 singletonObjects = new ConcurrentHashMap<>(256);
 
/** Cache of early singleton objects: bean name to bean instance. */
// 缓存半成品单例Bean。key:bean名字,value:bean实例(已实例化,但未注入属性和初始化)
private final Map earlySingletonObjects = new HashMap<>(16);
 
/** Cache of singleton factories: bean name to ObjectFactory. */
// 缓存单例bean的工厂。key:bean名字,value:能够生成bean的工厂
private final Map> 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_第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 三级缓存解决bean循环依赖,为何用三级缓存而非二级_panda9527z的博客-CSDN博客(看第一条评论)

spring循环依赖为什么要使用三级缓存_蓝天白云-CSDN博客_spring循环依赖为什么是三级缓存
【十七】Spring IOC 三级缓存解决循环依赖_Sid小杰的博客-CSDN博客_ioc三级缓存

 Spring原理--AOP_IT利刃出鞘的博客-CSDN博客

结论

不可以去掉第三级缓存。

原因

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

  1. 为了解决循环依赖但又尽量不打破这个设计原则的情况下,使用了第三级缓存(key:bean名字,value:ObjectFactory)。
    1. 前边分析过,它是将一个函数式接口作为ObjectFactory,相当于延迟初始化。AOP中发生循环依赖时,通过调用Object的getObject()方法获取到二级缓存中的对象。
  2. 如果去掉第三级缓存,将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_第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);
}

这里把对象放入第二级缓存。

Spring原理--AOP_feiying0canglang的博客-CSDN博客  曾分析过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,否则,返回代理类。

你可能感兴趣的:(循环依赖,Autowired,原理,SpringBoot)