闷棍暴打面试官 Spring源码系列: (一) Spring 如何解决循环依赖

2,194 阅读28分钟

前言

初夏时节, 时间: AM 7.30分左右, 空无一人的健身房里,一个硕大的身体在跑步机上扭动着, 不一会头上便挥汗如雨, 他嘴上还不时嘀咕着 "循环依赖,单例模式,Bean的定位加载注册,原型模式...", 渐渐跑着跑着就变成了慢慢悠悠的走走歇歇,忽然间感觉肩膀好像被砖头砸了一下,

身后传来一句 "你张大胖减肥是不可能减肥的,就是在健身房里划划水才能维持的了生活 !" ,

大胖被拍的浑身一惊, 看了眼一身运动装的 Mason, 急忙反驳道 "没有调查就没有发言权, 俺只是暂时休息下, 今天都已经跑了好几个小时了呢,倒是 Mason 你像是来假健身的...",

Mason 笑笑说 "哎呀呀, 你这么厉害呢,可惟独肚子上的赘肉骗不了人啊,最近面试的怎么样?"

大胖 一脸愁容的说道: "最近招聘市场很给力,我也参加了不少面试,就是每次聊到 Spring 时被面试官三连追问 Spring是如何解决循环依赖, 而我对着个问题查了很多资料,但也就能回答个一知半解,然后就叫我回去等通知了..."

Mason 在跑步机上边跑边说 "Spring 解决循环依赖的这个场景, 其实也可以映射到生活中, 比如 你工作日睡过了又害怕迟到扣钱,就在 DD出行 App上选择一个起始地打车, 同一时间来了两辆 DD专车, 你也没有check车牌就上车了, 司机也没check你拉上就走,你一上车就聚精会神看起宅舞视频, 结果到达别人的目的地发现上错车,既迟到了又要付来回路费,那么问题来了 你为什么会上错车呢? "

大胖 挠挠头回答道: "因为睡过了怕迟到啊! 呸,不对, 因为要早起去打工!"

Mason 白了张大胖一眼说: "起不来是因为你天天熬夜,坐错车是因为两个原因: 1.没有check车牌 2.DD专车不止一辆"

大胖 一脸懵X 的问道 "绕了半天, 那这上错车和Spring创建Bean时的循环依赖又有什么关系呢 ?"

Mason 一脸这孩子没救了的表情回答道 "循环依赖的触发条件就是, 你上了别人的DD专车, 而你打DD专车, 判断是否循环依赖就需要 check车牌,如果要彻底根治循环依赖就必须让世界上的 DD专车 只有一辆. Spring 中的循环依赖也是同理!"

DD出行

见微知著

我们暂不讨论 Spring 的循环依赖, 先看一道 LeetCode 题目 141. 环形链表 扩展: 在多线程环境下使用JDK1.7中的HashMap, 并发调用resize()时会出现环形链表,后再get()会导致CPU 100%, 那我们该如何去判断环形链表呢?

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 1

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 2

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

示例 3

那么 Spring 中是用那种方式发现 循环依赖的呢 ? (文章结尾揭晓答案)

//  Definition for singly-linked list.
public class ListNode {
    public int val;
    public ListNode next;
    public ListNode(int x) {
        val = x;
    }
}

Set判重

  public boolean hasCycle_Set(ListNode head) {

        // 如果入参链表过短则不存在环
        if (head == null || head.next == null) {
            return false;
        }

        HashSet<ListNode> set = new HashSet<>();

        // 如果 遍历到最后任然没有发现环则不存在环
        while (head.next != null){

            // 将每一个遍历过的元素存入,之后判重
            if (set.contains(head)){
                return true;
            }

            set.add(head);
            head = head.next;
        }

        return false;
    }

快慢指针判重

 public boolean hasCycle_QuickSlowPointer (ListNode head) {

        // 如果入参链表过短则不存在环
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head;

        // 如果 快指针遍历到最后仍然没有发现环则不存在环
        while (fast.next != null && fast.next.next != null){
            slow = slow.next;
            fast = fast.next.next;

            // 快指针每轮走两步 慢指针每轮走一步 如果有环快慢指针最终就会相遇
            if (slow == fast){
                return true;
            }
        }
        return false;
    }

新整了个活, 今后长期维护的一个LeetCode题解库,欢迎 Star

LeetCode 渐进式题解库: 让天下没有难刷的题 (Java)

LeetCode 渐进式题解库: 刷题使我快乐,自律使我自由!

Spring 中常用的两种 Bean DI 方式的循环依赖示例

Spring DI (Dependency Injection 依赖注入) 是指 Bean 被动接受其他 Bean的依赖注入而不自己主动去找, 简而言之 Bean 不会从容器中查找它依赖的 Bean , 而是靠容器实例化 Bean 时由容器将它依赖的 Bean 注入, 此举与Java 类实例化流程相反.

构造器 DI 示例代码(基于Spring 5.1.6)

package org.springframework.context.annotationX.circular.constructor;

import org.springframework.stereotype.Component;

/**
 * Spring Constructor DI 循环依赖  Bean1 Demo
 */
@Component
public class Bean1ConstructorBean2Demo {

   private Bean2ConstructorBean1Demo bean2;

    public Bean1ConstructorBean2Demo(Bean2ConstructorBean1Demo bean2DependBean1Demo) {
        this.bean2 = bean2DependBean1Demo;
    }

    public void hello() {
        bean2.hello();
    }
}

package org.springframework.context.annotationX.circular.constructor;

import org.springframework.stereotype.Component;

/**
 * Spring Constructor DI  循环依赖  Bean2 Demo
 */
@Component
public class Bean2ConstructorBean1Demo {

	private Bean1ConstructorBean2Demo bean1;

	public Bean2ConstructorBean1Demo(Bean1ConstructorBean2Demo bean1DependBean2Demo1) {
		bean1 = bean1DependBean2Demo1;
	}

	public void hello() {
		System.out.println("Run Circular Dependency Success");
	}
}

注解自动装配 DI 示例代码

package org.springframework.context.annotationX.circular.autowired;

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

/**
 * Spring  @Autowired DI 循环依赖  Bean1 Demo
 */
@Component
public class Bean1AutowiredBean2Demo {

   @Autowired
   private Bean2AutowiredBean1Demo bean2;

    public void hello() {
        bean2.hello();
    }
}
package org.springframework.context.annotationX.circular.autowired;

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

/**
 * Spring  @Autowired DI  循环依赖  Bean2 Demo
 */
@Component
public class Bean2AutowiredBean1Demo {

    @Autowired
   private Bean1AutowiredBean2Demo bean1;

    public void hello(){
        System.out.println("Run Circular Dependency Success");
    }
}

两种 DI 方式的单元测试代码

package org.springframework.context;


import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotationX.circular.autowired.Bean1AutowiredBean2Demo;
import org.springframework.context.annotationX.circular.constructor.Bean1ConstructorBean2Demo;

/**
 * Created by 以斗争求团结则团结存,以退让求团结则团结亡 !
 */
public class AnnotationCircularDependencyTestX {

	@Test
	public void diBeanByAutowired() throws Exception {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.scan("org.springframework.context.annotationX.circular.autowired");
		context.refresh();
		Bean1AutowiredBean2Demo bean1 = (Bean1AutowiredBean2Demo) context.getBean("bean1AutowiredBean2Demo");
		bean1.hello();
	}

	@Test
	public void diBeanByConstructor () throws Exception {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.scan("org.springframework.context.annotationX.circular.constructor");
		context.refresh();
		Bean1ConstructorBean2Demo bean1 = (Bean1ConstructorBean2Demo) context.getBean("bean1ConstructorBean2Demo");
		bean1.hello();
	}
}

猜猜上面那种 DI 方式打印了 "Run Circular Dependency Success" 以及它解决循环依赖的方式 ?

答案: 注解自动装配 DI 示例代码 打印成功 而 构造器 DI 示例代码因为循环依赖运行失败 , 那这两种方式有什么区别呢 !

(构造器 DI 错误日志)

 Unsatisfied dependency expressed through constructor parameter 0; nested exception is
 org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name
 'bean2ConstructorBean1Demo' defined in file
 [D:\SpringFamily-SourceCodeStudy\Spring-Framework\spring-framework-5.1.6.REL
 EASE\spring-context\out\test\classes\org\springframework\context\annotationX\circular\constructor\Bean2Const
 ructorBean1Demo.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is
 org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name
 'bean1ConstructorBean2Demo': Requested bean is currently in creation: Is there an unresolvable circular
 reference?
 
 at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1341)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1187)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
	....
  • 注解自动装配 DI 方式

    • 优点:

      • 基于三层缓存不会发生循环依赖

      • 自描述清晰

    • 缺点:

      • 对构建单元测试不友好
  • 构造器 DI 方式

    • 优点:
      • 代码结构明确,可读性高
      • 构建单元测试友好
      • 非IOC容器环境可使用new实例化该类的对象。
    • 缺点:
      • 会发生循环依赖
      • 当注入参数较多时,代码臃肿。

对于循环依赖问题,Spring根据注入方式,采取不同的处理策略,如果依赖双方都是使用属性值注入或者Setter方法注入,则Spring可以自动解决循环依赖注入问题,Spring 程序可以成功启动;如果依赖双方是使用构造函数注入对方或者主Bean对象使用构造函数注入或循环注入的Bean都是原型模式,则Spring 无法解决循环依赖注入 ,Spring程序报循环依赖无法启动。

注解 DI 方式 的运行成功原因

注解 DI 方式与 构造器 DI 方式 最终要达到的目的相同, 但Spring 对它俩的实现却不太一样, Spring-Context 在 AbstractApplicationContext.refresh() 方法完成 Bean 的关键生命周期 IOC, 实例化, DI 等, DI 具体是 IOC 完成后调用 finishBeanFactoryInitialization() 方法, 具体方法逻辑如下

  • 能触发依赖注入的条件有俩: 1. 第一次调用 getBean 方法时, 2. 懒加载被预实例化时, 此方法满足了其中第一条.

  • 1.根据 beanDefinitionMap 依次判断 Bean 是否 Lazy, 是否 Prototype, 是否 Abstract 等等

  • 2.接下来根据判断结果 填充构造方法来反射 Bean 的从而实例化. 所以在此之前必须推断Bean的构造方法.

  • 3.反射实例化一个对象;注意我这里说的是对象、对象、对象;不是并不是一个完整的bean,因为 对象属性是没有注入,所以不是一个完整的bean;

  • 4.Spring 处理 合并后的 BeanDefinition.

  • 5.判断是否支持 循环依赖 如果支持则提前把 一个工厂存入 singletonFactories Map<String, ObjectFactory<?>>

  • 6.进行属性注入

  • 7.回调 Aware 接口, 生命周期回调方法, 是否需要代理

  • 8.put 到 Spring容器

  • 文章最后会带大家详细阅读上述源码.

成功原因:

// 是否需要提前曝光,用来解决循环依赖时使用
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");
	}
	// 这里是一个匿名内部类, 为了循环引用, 尽早持有对象的引用	
    // 解决循环依赖 第二个参数是回调接口,实现的功能是将切面动态织入 bean
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            // 判断 singletonObjects 不存在 beanName
            if (!this.singletonObjects.containsKey(beanName)) {
            // 放入 beanName -> beanFactory,到时在 getSingleton() 获取单例时,可直接获取创建对应 bean 的工厂,解决循环依赖
            this.singletonFactories.put(beanName, singletonFactory);
            // 从提前曝光的缓存中移除,之前在 getSingleton() 放入的
            this.earlySingletonObjects.remove(beanName);
            // 往注册缓存中添加 beanName
            this.registeredSingletons.add(beanName);
        }
    }
}

先来看 earlySingletonExposure 这个变量: 从字面意思理解就是需要提前曝光的单例

有以下三个判断条件:

  • mbd 是否是单例
  • 该容器是否允许循环依赖
  • 判断该 bean 是否在创建中。

如果这三个条件都满足的话,就会执行 addSingletonFactory 操作。要想着,写的代码都有用处,所以接下来看下这个操作解决的什么问题和在哪里使用到吧

Bean1AutowiredBean2Demo 类中含有属性 Bean2AutowiredBean1DemoBean2AutowiredBean1Demo 类中含有属性 Bean1AutowiredBean2Demo,这两个类在初始化的时候经历了以下的步骤:

  1. 创建 Bean1AutowiredBean2Demo,先记录对应的 beanName 然后将 Bean1AutowiredBean2Demo创建工厂 beanFactoryA 放入缓存中
  2. Bean1AutowiredBean2Demo的属性填充方法 populateBean,检查到依赖 Bean2AutowiredBean1Demo,缓存中没有 Bean2AutowiredBean1Demo 的实例或者单例缓存,于是要去实例化 Bean2AutowiredBean1Demo
  3. 开始实例化 Bean2AutowiredBean1Demo,经历创建 Bean2AutowiredBean1Demo的过程,到了属性填充方法,检查到依赖了 Bean1AutowiredBean2Demo
  4. 调用 getBean(Bean1AutowiredBean2Demo) 方法,在这个函数中,不是真正去实例化 Bean1AutowiredBean2Demo,而是先去检测缓存中是否有已经创建好的对应的 bean,或者已经创建好的 beanFactory
  5. 检测到 beanFactoryA 已经创建好了,而是直接调用 ObjectFactory 去创建 Bean1AutowiredBean2Demo

为什么注解DI 可以拿到之前的缓存呢?

  • 因为你已经通过空参实例化完成了 Bean1AutowiredBean2Demo 并存在beanFactoryA中, Bean2AutowiredBean1Demo再来拿的话就通过 getBean->getSingleton()拿到 beanFactoryA后注入到属性中既可完成 Bean的初始化, 而这一步是在实例化之后, 而用构造器DI 的话, Bean1AutowiredBean2Demo 实例化时会发生循环依赖.
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

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


	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// 从单例池 (一级缓存) 中直接拿 Bean
		Object singletonObject = this.singletonObjects.get(beanName);
		// 一级缓存中没有 该Bean, 并且当前 BeanName 在正在创建的 Set 集合中
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				// 从三级缓存拿 Bean, 原因: 第一次其实是拿不到的, 因为现在只有 二级缓存中 存了一个 工厂对象, 所以成立向三级缓存添加
				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;
	}
}

构造器 DI 方式 运行失败的原因

构造方法DI 抛出异常前调用堆栈信息

失败原因: 根据上述堆栈可以分析, autowireConstructor() 试图通过构造方法反射 bean1ConstructorBean2Demo 实例时它必须先实例化 bean2ConstructorBean1Demo 然后依次循环, 走到 getSingleton() -> DefaultSingletonBeanRegistry.beforeSingletonCreation() 方法时就检测出异常, 那么为什么这种方式没有像 注解 DI 那样解决问题呢 ?

  • 注解DI方式 与 构造器DI方式最大的区别在与 AbstractAutowireCapableBeanFactory.createBeanInstance() 中的实例化策略不太一样. 从各自定义SpringBean的源代码上看, 构造器DI方式 需要申明当前类的构造器以及依赖的类, 而 注解DI方式则不需要 (默认空参)
  • 构造器DI: return autowireConstructor(beanName, mbd, ctors, args); 使用容器的自动装配特性, 调用匹配的构造方法进行实例化
  • 注解DI: return instantiateBean(beanName, mbd); 使用默认的无参构造方法进行实例化
  • 如上图所示, 调用堆栈 在 createBeanInstance() 实例化方法中发生了循环引用, 并没有执行到 populateBean()进行依赖注入. 为什么会发生这一切?

在此之前建议阅读一下 Spring官方对循环依赖的文档

DI时 thorw BeanCurrentlyInCreationException 代码片段

// 构造方法DI getSingleton() 中 thorw BeanCurrentlyInCreationException
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	// 当前 Bean 不存在可排除的 inCreationCheckExclusions && 当前 Bean 之前已存在于 则 thorw singletonsCurrentlyInCreation 中
	protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}
    
    protected void afterSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}
}

构造方法DI thorw 的触发原因: 两次 beforeSingletonCreation() 同一个Bean, 因为如果是没有发生循环依赖的话接下来会执行 afterSingletonCreation(beanName) 清除本轮 singletonsCurrentlyInCreation.remove(beanName) 但在 beforeSingletonCreation--> 递归 autowireConstructor() <--afterSingletonCreation 因此触发.

// 原型BeanDI doGetBean 中 thorw BeanCurrentlyInCreationException
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
    
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
    	@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    
      // 如果之前创建过相同的原型Bean 则 thorw
        if (isPrototypeCurrentlyInCreation(beanName)) {
          throw new BeanCurrentlyInCreationException(beanName);
        }
    
        /** 创建原型模式 Bean 的实例对象*/
		 if (mbd.isPrototype()) {
			// 原型模式 (Prototype) 每次都会创建一个新的对象
			Object prototypeInstance = null;
		try {
			// 回调 beforePrototypeCreation() 方法, 默认的功能是注册当前创建的原型对象
			beforePrototypeCreation(beanName);
			// 创建指定 Bean 的对象实例
			prototypeInstance = createBean(beanName, mbd, args);
		 }
			finally {
			// 回调 afterPrototypeCreation() 方法, 默认的功能是告诉 IOC 容器 不再创建指定 Bean 的原型对象
			afterPrototypeCreation(beanName);
			   	    	}
		    	bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
		 }
        ...
    }
} 

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
	private final ThreadLocal<Object> prototypesCurrentlyInCreation =
			new NamedThreadLocal<>("Prototype beans currently in creation");

    
    protected boolean isPrototypeCurrentlyInCreation(String beanName) {
		Object curVal = this.prototypesCurrentlyInCreation.get();
		return (curVal != null &&
				(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
	}


    protected void beforePrototypeCreation(String beanName) {
		Object curVal = this.prototypesCurrentlyInCreation.get();
		if (curVal == null) {
			this.prototypesCurrentlyInCreation.set(beanName);
		}
		else if (curVal instanceof String) {
			Set<String> beanNameSet = new HashSet<>(2);
			beanNameSet.add((String) curVal);
			beanNameSet.add(beanName);
			this.prototypesCurrentlyInCreation.set(beanNameSet);
		}
		else {
			Set<String> beanNameSet = (Set<String>) curVal;
			beanNameSet.add(beanName);
		}
	}   

    protected void afterPrototypeCreation(String beanName) {
		Object curVal = this.prototypesCurrentlyInCreation.get();
		if (curVal instanceof String) {
			this.prototypesCurrentlyInCreation.remove();
		}
		else if (curVal instanceof Set) {
			Set<String> beanNameSet = (Set<String>) curVal;
			beanNameSet.remove(beanName);
			if (beanNameSet.isEmpty()) {
				this.prototypesCurrentlyInCreation.remove();
			}
		}
	}
}

原型BeanDI thorw 的触发原因: 两次 beforePrototypeCreation() 同一个Bean, 因为如果是没有发生循环依赖的话接下来会执行 afterPrototypeCreation(beanName) 清除本轮 prototypesCurrentlyInCreation.remove() 但在 beforePrototypeCreation--> 递归 doGetBean() <--afterPrototypeCreation 因此触发.

回溯几个有意思的问题

Spring是如何发现循环依赖的?

巧妙的用了LeetCode[141]中 Set 解法 把Bean 的加载顺序当作一个单向链表边存入边判重.

Spring的注解DI方式 是如何解决循环依赖的 ?

因为使用 注解DI 代码风格上是没有构造函数的, 在AbstractAutowireCapableBeanFactory.createBeanInstance() 走空参构造进行实例化, 所以不需要去像构造器DI 那样去实例化别的类, 然后在 populateBean() 中进行属性注入, 这时候已经完成实例化了要进行依赖注入了, 构造器DI方式就是在 实例化的时候翻车的呀, 具体怎么进行循环依赖的属性注入就靠 二 三级缓存咯.

  • 一级缓存: singletonObjects 它是我们最熟悉的朋友,俗称“单例池”“容器”,缓存创建完成单例Bean的地方, 也可以称之为 Spring 容器.

  • 二级缓存: singletonFactories 映射创建Bean的原始工厂

  • 三级缓存: earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.

千言万语都在gif图里, 感谢作者vt 授权...

修复构造器 DI 引发的循环依赖的补丁有那几种 ?

  • 在主构造DI 方法上加上 @Lazy, Spring会动态代理创建代理类来解决
  • 在发生循环依赖的注入主类上加上 @Autowired, 并记得删除构造函数
  • 实现 InitializingBean, ApplicationContextAware 接口, 在Spring 用 InitializingBean 时手动获取容器注入.
  • 更多

Spring Bean 是如何被创建的 ?

在学习 Spring 时你可以把它比作一家 糖果工厂 , 糖果工厂里很多条夹心软糖生产线, 其中最赚钱的两条夹心软糖生产线, 分别是A生产线 与X生产线, A生产线生产软糖要必备一种叫@注解的糖浆原料, X 生产线则要必备另一种叫<!--Xml 的糖浆原料, 众所周知软糖生产有四大工艺 定位 -> 加载 -> 注册->DI 在这点上它俩除了 定位, 加载 不一样其他工艺则相同, 这两条生产线虽然工序,原料不同但生产的夹心软糖口感一致, 不过 A生产线软糖包装上选用了市面上 流行的透明塑料包装, 透着一股子干净简练, 而 X生产线 软糖包装则用的是白卡纸包裹的严严实实, 每次顾客拆包装都要费点工夫, X 生产线 是厂里的元老了, 诞生于 百糖争鸣的 Servlet 2.0 年代, 当年它的粉丝都是一水的棒小伙, 乖小妹, 现在爱吃它的还同一批人 但都变成了老头老太, 外加一批 软糖考古学者, A 生产线 崛起于 和平盛世的 Servlet 3.0 年代, 它紧跟时代潮流, 潮流里的后浪也紧跟着它, 无数青少年吃着它跨入不惑之年. 年轻人 ,你想听我讲讲这两条生产线的生产软糖的故事吗?

A 生产线 (AnnotationConfigApplicationContext) UML

AnnotationConfigApplicationContext UML类图

X 生产线 (ClassPathXmlApplicationContext) UML

ClassPathXmlApplicationContext UML

准备生产所需的原料

package org.springframework.context.softsweetsX;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.ZoneId;

@Component
public class SoftSweetsBean implements InitializingBean, ApplicationContextAware {
	private String productionLineName;
	private String dateManufacture;

	public String getProductionLineName() {
		return productionLineName;
	}

	public String getDateManufacture() {
		return dateManufacture;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		this.dateManufacture = Instant.now().atZone(ZoneId.systemDefault()).toString();
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		 this.productionLineName = applicationContext.getDisplayName();
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">
<!--SoftSweetsX.xml-->
<beans>
	<bean id="softSweetsBean" class="org.springframework.context.softsweetsX.SoftSweetsBean"/>
</beans>

两种软糖的初体验

import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SoftSweetsTest {

	@Test
	public void A_ProductLine() {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.scan("org.springframework.context.softsweetsX");
		context.refresh();
		SoftSweetsBean bean = (SoftSweetsBean) context.getBean("softSweetsBean");
		System.out.println("\n新鲜出炉的A软糖 ~~~ \n 生产线名称: " + bean.getProductionLineName() + "\n 生产日期: " + bean.getDateManufacture());
	}

	@Test
	public void X_ProductLine() {
		// test-resources
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("org/springframework/beans/factory/xml/SoftSweetsX.xml");
		SoftSweetsBean bean = (SoftSweetsBean) context.getBean("softSweetsBean");
		System.out.println("\n新鲜出炉的X软糖 ~~~ \n 生产线名称: " + bean.getProductionLineName() + "\n 生产日期: " + bean.getDateManufacture());
	}
}

控制台输出结果

新鲜出炉的X软糖 ~~~ 
 生产线名称: org.springframework.context.support.ClassPathXmlApplicationContext@525b461a
 生产日期: 2020-05-28T13:57:23.738+08:00[Asia/Shanghai]

新鲜出炉的A软糖 ~~~ 
 生产线名称: org.springframework.context.annotation.AnnotationConfigApplicationContext@10db82ae
 生产日期: 2020-05-28T13:57:24.784+08:00[Asia/Shanghai]

如果你对生产软糖感兴趣 来吧🦃 -> SpringFamily-SourceCodeStudy

Bean 加载步骤

  • IOC (Inversion of Control 控制反转)

    • 定位 (确定原料位置)
    • 加载 (找到原料后提取为可注册 BeanDefinition)
    • 注册 (将 BeanDefinition 校验后注册到 Map<String, BeanDefinition> beanDefinitionMap)
  • DI (Dependency Injection 依赖注入)

    • 实例化 (反射 new 对象)ioc
    • 依赖注入 (容器主动查找 bean 依赖)
IOC 控制反转

ioc

两条生产线的 定位加载 代码

// X 生产线
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
    
    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}
    
    public ClassPathXmlApplicationContext(
                String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
                throws BeansException {
			// 设置容器资源加载器
            super(parent);
		    /* <定位/>  将配置的Bean信息为 Spring 封装的 Resource */
            setConfigLocations(configLocations);
            if (refresh) {
                refresh();
            }
        }
    
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
    
    @Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// 创建 XmlBeanDefinitionReader, 即创建 Bean 读取器
		// <加载/> 并通过回调设置到容器中, 容器使用该读取器读取 Bean 配置资源
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// 为 Bean 读取器设置 Spring 资源加载器
		// AbstractXmlApplicationContext 的祖先父类 AbstractApplicationContext 继承 DefaultResourceLoader
		// 因此容器本身也是一个资源加载器
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		// 为Bean 读取器设置 SAX xml 解析器
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// 当Bean 读取器读取 Bean 定义 xml 资源文件时, 启用 xml 的校验机制
		initBeanDefinitionReader(beanDefinitionReader);
		// Bean 读取器真正实现加载的方法
		loadBeanDefinitions(beanDefinitionReader);
	}
}
    
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    	
            // 按照 Spring 的Bean 语义要求将 Bean 配置信息解析并转换为容器内部数据结构
        public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
            /* 得到 BeanDefinitionDocumentReader 来对 XML 格式的 BeanDefinition 进行解析*/
            BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
            // 获得容器中注册的 Bean 总数 (包含内置 bean)
            int countBefore = getRegistry().getBeanDefinitionCount();
            // 解析过程的入口, 这里使用了委派模式, BeanDefinitionDocumentReader 只是一个接口
            /* <注册/> 具体的解析过程由实现类 DefaultBeanDefinitionDocumentReader 完成 */
            documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
            // 统计解析的 Bean 数量
            return getRegistry().getBeanDefinitionCount() - countBefore;
        }
    }
}

// ----------------------------------------------- 分割线 ---------------------------------------------------

// A 生产线
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
    public void scan(String... basePackages) {
            Assert.notEmpty(basePackages, "At least one base package must be specified");
            /* <定位/> */
        	this.scanner.scan(basePackages);
    }
}

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
    
    public int scan(String... basePackages) {
        // 获得容器中注册的 Bean 总数 (包含内置 bean)
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
		
		doScan(basePackages);

		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}
		
       // 统计解析的 Bean 数量
		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}
    
   	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		for (String basePackage : basePackages) {
            /* <加载/> */
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					/* <注册/> */
                    registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

}

上述代码 表示 两条生产线定位 载入 是不同的, 但从 UML 类图看 它俩 都继承了 AbstractApplicationContext 所以 注册 DI 是相同的

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

    // 存储注册信息 BeanDefinition
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    
		@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		// 校验解析的 beanDefinition
		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		if (existingDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (logger.isInfoEnabled()) {
					logger.info("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							existingDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(existingDefinition)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + existingDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			if (hasBeanCreationStarted()) {
				// 注册的过程中需要线程同步, 以保证数据的一致性
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		// 检查是否已经注册过同名的 beanDefinition
		if (existingDefinition != null || containsSingleton(beanName)) {
			// 重置所有已经注册过的 beanDefinition 缓存
			resetBeanDefinition(beanName);
		}
	}
}    

总揽全局, 可以看到我们讲的 IOCDI 只是众多 Spring 生命周期中的一部分.

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 1. 调用容器准备的刷新方法, 获取容器的当前时间, 同时给容器设置同步标识
			prepareRefresh();

			// 2. <X 生产线 IOC/> 告诉子类启动 refreshBeanFactory()方法, Bean定义资源文件的载入从子类的 refreshBeanFactory() 方法启动
			// 继承了 AbstractRefreshableApplicationContext 的容器子类可以调用, 从 UML 图上看 X 生产线可以,A 生产线不行
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 3. 为 BeanFactory 配置容器特性, 例如类加载器, 事件处理器等
			prepareBeanFactory(beanFactory);

			try {
				// 4. 为容器的某些子类指定的特殊的 Post 事件处理器
				postProcessBeanFactory(beanFactory);

				// 5. <A 生产线 IOC/> 调用所有注册的 BeanFactoryPostProcessor 的 Bean
				invokeBeanFactoryPostProcessors(beanFactory);

				// 6. 为 BeanFactory 注册 Post 事件处理器
				// BeanPostProcessor 是Bean 后置处理器, 用于监听容器触发的事件
				registerBeanPostProcessors(beanFactory);

				// 7. 初始化信息源, 和国际化相关
				initMessageSource();

				// 8. 初始化容器事件传播器
				initApplicationEventMulticaster();

				// 9. 调用子类的某些特殊的Bean的初始化方法
				onRefresh();

				// 10. 为事件传播器注册事件监听器
				registerListeners();

				// 11. <DI/> 初始化所有剩余的单例模式Bean (non-lazy-init)
				finishBeanFactoryInitialization(beanFactory);

				// 12. 初始化容器的生命周期事件处理器,并发布容器的生命周期事件
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// 13. 销毁已经创建的单例Bean,以避免挂起资源。
				destroyBeans();

				// 14. 取消刷新操作, 重置容器的同步标识
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// 15. 重设公共缓存, 可能再也不需要单例bean的元数据了……
				resetCommonCaches();
			}
		}
	}	
}
DI 依赖注入

di

循环依赖就发生在 DI 依赖注入这一步. 接下来我们详细探讨一下 它的原理. 看千遍不如手动搞一遍, 不然只是别人的知识,

依赖注入触发规则

  • 用户第一次调用 getBean 方法时, IOC 容器触发依赖注入
  • Bean 设置为 懒加载, 在需要预实例化 Bean 时触发依赖注入

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {

   // 11. <DI/> 初始化所有剩余的单例模式Bean (non-lazy-init)
   protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		...
       beanFactory.preInstantiateSingletons();
	}   
}

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

	@Override
	public void preInstantiateSingletons() throws BeansException {
	
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// 触发所有非惰性单例bean的实例化…
		for (String beanName : beanNames) {
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					... 
				}
				else {
                    // <实例化/> 第一次调用 getBean 方法时, IOC 容器触发当前 Bean的依赖注入与实例化
					getBean(beanName);
				}
			}
		}
        ...
	}
}


public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    @Override
	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}
    
    	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
		@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

			// 根据指定的名称获取被管理的 Bean 名称, 剥离指定名称中对容器的相关依赖
			// 如果指定的是别名, 将别名转换为 规范的 Bean 名称
			final String beanName = transformedBeanName(name);
			Object bean;

			// 先从缓存中读取是否已经有被创建过的单例模式的 Bean
			// 对于单例模式的Bean 整个 IOC 容器中只创建一次, 不需要重复创建
			Object sharedInstance = getSingleton(beanName);
			// IOC 容器创建单例模式的 Bean 示例对象
			if (sharedInstance != null && args == null) {
				if (logger.isTraceEnabled()) {
					// 如果在容器中已有指定名称的单例模式 Bean 被创建, 直接返回已经创建的 Bean
					if (isSingletonCurrentlyInCreation(beanName)) {
						logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
								"' that is not fully initialized yet - a consequence of a circular reference");
					}
					else {
						logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
					}
				}
				// 注意: FactoryBean 是创建对象的工厂 Bean, BeanFactory 是管理 Bean 的工厂
				// 获取给定 Bean 的实例对象, 主要完成 FactoryBean 的相关处理
				bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
			}

			else {
				// 缓存中没有正在创建的 单例模式的 Bean
				// 缓存中已有原型模式的 Bean
				// 但是由于循环依赖导致实例化对象失败
				if (isPrototypeCurrentlyInCreation(beanName)) {
					throw new BeanCurrentlyInCreationException(beanName);
				}

				// 对 IOC 容器中是否存在指定名称的 BeanDefinition 进行检查
				// 首先检查是否能对当前的 BeanFactory 中获取所需要的 Bean,
				// 如果不能则委托当前容器的父容器去查找, 如果还是找不到则沿着容器的继承体系向父容器查找
				BeanFactory parentBeanFactory = getParentBeanFactory();
				if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
					// 解析指定 Bean 名称的原始名称
					String nameToLookup = originalBeanName(name);
					if (parentBeanFactory instanceof AbstractBeanFactory) {
						return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
								nameToLookup, requiredType, args, typeCheckOnly);
					}
					else if (args != null) {
						// 委托父容器根据指定名称和显式的参数查找
						return (T) parentBeanFactory.getBean(nameToLookup, args);
					}
					else if (requiredType != null) {
						// 委托父容器根据指定 名称和类型查找
						return parentBeanFactory.getBean(nameToLookup, requiredType);
					}
					else {
						// 委托父容器根据指定 名称查找
						return (T) parentBeanFactory.getBean(nameToLookup);
					}
				}

				// 创建的 Bean 是否需要进行类型验证, 一般不需要
				if (!typeCheckOnly) {
					// 向容器标记指定的 Bean 已经被创建
					markBeanAsCreated(beanName);
				}

				try {
					// 根据指定 Bean 名称获取其父级 Bean 定义
					// 主要解决 Bean 继承子类和父类公共属性问题
					final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
					checkMergedBeanDefinition(mbd, beanName, args);

					// 获取当前 Bean 所有依赖 Bean 的名称
					String[] dependsOn = mbd.getDependsOn();
					/** 如果当前 Bean 有 @DependsOn 依赖的 Bean */
					if (dependsOn != null) {
						for (String dep : dependsOn) {
							if (isDependent(beanName, dep)) {
								throw new BeanCreationException(mbd.getResourceDescription(), beanName,
										"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
							}
							// 把被依赖 Bean 注册给当前依赖的 Bean
							registerDependentBean(dep, beanName);
							try {
								// 递归调用 getBean()方法, 获取给当前依赖 Bean
								getBean(dep);
							}
							catch (NoSuchBeanDefinitionException ex) {
								throw new BeanCreationException(mbd.getResourceDescription(), beanName,
										"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
							}
						}
					}

					/** 创建单例模式的 Bean 的实例对象*/
					if (mbd.isSingleton()) {
						// 这里使用了一个匿名的内部类创建 Bean 实例对象, 并且注册给所依赖的对象
							sharedInstance = getSingleton(beanName, () -> {
								try {
									// 创建一个指定的 Bean 的实例对象, 如果有父级继承, 则会合并子类和父类的定义
									return createBean(beanName, mbd, args);
								}
								catch (BeansException ex) {
									// Explicitly remove instance from singleton cache: It might have been put there
									// eagerly by the creation process, to allow for circular reference resolution.
									// Also remove any beans that received a temporary reference to the bean.
									// 显式地从容器中单例模式的 Bean 缓存中清除实例对象
									destroySingleton(beanName);
									throw ex;
								}
							});
						// 获取给定的 Bean 实例对象
						bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
					}

					/** 创建原型模式 Bean 的实例对象*/
					else if (mbd.isPrototype()) {
						// 原型模式 (Prototype) 每次都会创建一个新的对象
						Object prototypeInstance = null;
						try {
							// 回调 beforePrototypeCreation() 方法, 默认的功能是注册当前创建的原型对象
							beforePrototypeCreation(beanName);
							// 创建指定 Bean 的对象实例
							prototypeInstance = createBean(beanName, mbd, args);
						}
						finally {
							// 回调 afterPrototypeCreation() 方法, 默认的功能是告诉 IOC 容器 不再创建指定 Bean 的原型对象
							afterPrototypeCreation(beanName);
						}
						bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
					}

					/**  创建 Web Bean 的实例对象, 比如 request , session, application 等生命周期*/
					else {
						// 要创建的 Bean 既不是单例模式的, 也不是原型模式的, 则根据 Bean 定义资源中
						// 配置的生命周期范围, 选择实例化 Bean 的合适方法, 这种方式在多用于 Web 应用程序中
						String scopeName = mbd.getScope();
						final Scope scope = this.scopes.get(scopeName);
						// 如果Bean 定义资源中没有配置生命周期范围, 则Bean定义不合法
						if (scope == null) {
							throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
						}
						try {
							// 这里又使用了一个匿名内部类, 获取一个指定生命周期范围的实例
							Object scopedInstance = scope.get(beanName, () -> {
								beforePrototypeCreation(beanName);
								try {
									return createBean(beanName, mbd, args);
								}
								finally {
									afterPrototypeCreation(beanName);
								}
							});
							// 获取指定 Bean 的实例对象
							bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
						}
						catch (IllegalStateException ex) {
							....
						}
					}
				}
				catch (BeansException ex) {
					cleanupAfterBeanCreationFailure(beanName);
					throw ex;
				}
		}
...
	
		return (T) bean;
	}
}

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// 封装被创建的 Bean 对象
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}

		if (instanceWrapper == null) {
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		final Object bean = instanceWrapper.getWrappedInstance();
		// 获取实例化对象的类型
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// 调用 PostProcessor 后置处理器
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
				...
				}
				mbd.postProcessed = true;
			}
		}

		//  向容器中 缓存单例模式的 Bean 对象, 以防止循环引用
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			// 这里是一个匿名内部类, 为了循环引用, 尽早持有对象的引用
			// 第二个参数是回调接口,实现的功能是将切面动态织入 bean
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Bean 对象的初始化, 依赖注入在此触发
		Object exposedObject = bean;
		try {
			// 将 Bean 实例对象封装, 并且将 Bean 定义中配置的属性值赋给实例对象
			populateBean(beanName, mbd, instanceWrapper);
			// 调用初始化方法,例如 init-method
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		if (earlySingletonExposure) {
			// 获取指定名称的已注册的单例模式 Bean 对象
			Object earlySingletonReference = getSingleton(beanName, false);
			if (earlySingletonReference != null) {
				// 根据名称获取 的已注册的 Bean 和正在实例化的 Bean 是同一个
				if (exposedObject == bean) {
					// 当前实例化的 Bean 初始化完成
					exposedObject = earlySingletonReference;
				}
				// 当前 Bean 依赖其他 Bean, 并且当发生循环引用时不允许创建新的实例对象
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					// 获取当前 Bean 所依赖的其他 Bean
					for (String dependentBean : dependentBeans) {
						// 对依赖 Bean 进行类型检查
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
					if (!actualDependentBeans.isEmpty()) {
						...
					}
				}
			}
		}

		// 注册完成依赖注入的 Bean
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

扫码回复 "加群" 和我一起月入 20K+

深入浅出分享 Java 干货 , 找回对代码的 Passion , 助力月入 20K+

后话

Mason 能看懂源码,主要有两方面在起作用,

一方面是因为水滴石穿的不懈努力,客观上起了作用,

一方面是因为 Mason 通过读毛选掌握了克服困难的方法论, 主观上起了作用,

推荐大家参加 毛三公的B站活动 #我在读毛选#

感谢大家的积极参与!第一期的红军球5.15日晚上抽奖送出

[OK]
第二期的读毛 选投稿奖励范围: 1.学习老五篇的感想 2.结合自身情况说说对《青年运动的方向》一文的体会 期待大家新的投稿!

img