从源码的角度来看spring的循环引用的问题

0. 前言

0.1说明

以下的东西,都是我看源码总结出来的,并不是道听途说。大部分都是经过我验证的。如果有错误,那就是我源码没有看明白。有错误的地方,欢迎指出,谢谢。。。。。

0.2 为什么要学习spring 源码?

spring 源码我也看了一段时间了。那为什么要看spring 源码呢?
现在spring 发展的多厉害我不用多说吧,到处都离不开。

都知道:Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。。它的源代码无意是Java技术的最佳实践的范例。

那spring 的源码体系极其庞大,各种继承实现体系更不用说了。那家伙,能看懂就不错了。还想学习人家是怎么设计的,怎么运用各种设计模式的?我感觉,大部分人,看都费劲,分析人家的结构,还是算了。(除非水平特别的高)。

那我感觉,大部分人,看spring 源码之后,对spring 的流程与设计思想,都有了很深的认识。那我看spring 源码的目的,就是为了对spring更加的了解,更加的精通spring 。并不是说,看了人家的源码,自己写代码的水平就提高了。那也不太可能吧…… 所以说,我看spring源码就是为了对spring 更加的了解。了解其spring 的扩展点等地方,那么有朝一日,你开发了自己的组件,自己的框架,可以很顺利的整合到spring 中。现在,如果一个框架,连spring 都无法整合?你感觉这个框架可以被很多地方运用么?其他的不用多说了吧……

1. 一般情况

那么在了解 “spring如何解决循环引用情况下创建bean的?” 的问题之前呢,我感觉有必要先阐述一下:spring在一般情况下创建bean的大概流程。注意是大概流程!

1.1 图解

在这里插入图片描述

1.2 文字

为了方便进行代码的跳转,和方便阅读,我把图转换成文字。

  1. 进入doGetBean()方法,我们都知道,获取bean是从此方法开始的。

  1. 根据beanName首先去singletonObjects容器(Map)中去拿,如果没有就返回null。
    -----因为此bean并没有处于创建状态(没有在singletonsCurrentlyInCreation中)所以并不会去二级缓存,三级缓存中去拿。 代码详见附录 1

  1. (拿不到的情况)将beanName 放入singletonsCurrentlyInCreation
    ----singletonsCurrentlyInCreation 是一个list 里面存放着正在创建的bean的beanName,当bean创建完成,即将放入singletonObjects中之前,会从中移除。 代码详见 附录2

  1. 选择合适的构造方法对bean进行创建。详见代码附录3

  1. 放入中singletonFactories(三级缓存)
    ----并不是所有的都放入三级缓存,是有条件的;条件是:单例的,允许循环引用的,正在创建状态中的 放入缓存的作用是什么呢?(为了解决循环引用的问题。) 详见代码附录4

  1. 对其内的属性进行填充
    ----- 进行属性注入的时候,如果要注入的对象,还会调用getBean()进行获取(有,则拿到,没有则创建),其流程与这个相同。 如果要注入的对象,其内还依赖了一个其他的对象,则原理相同。详见代码附录 5

  1. 对bean应用相应的BeanPostProcessor,调用相应的Aware 的方法,调用相应的生命周期中的初始化方法等
    -----BeanPostProcessor 这种类型,不用我说,相信都知道,毕竟这东西很重要。
    Aware 看代码:
    生命周期中的初始化的方法比如:你通过xmlor 注解,声明一个 init-method ,或者是afterPropertiesSet()方法等。
    详见代码附录6

  1. beanNamesingletonsCurrentlyInCreation中删除将bean放入singletonObjects, 同时根据beanName将bean从二级缓存三级缓存中删除。
    详见代码附录7

2. 循环引用

2.1 什么是循环引用?

很简单,就是一个对象1 依赖对象2, 但是对象2呢,又依赖对象1。如图所示

在这里插入图片描述

2.2 前置代码准备

准备两个类,让其循环引用,其代码如下:

  • BeanOne
@Component
public class BeanOne {

    @Autowired
    private BeanTwo beanTwo;
}
  • BeanTwo
@Component
public class BeanTwo {

    @Autowired
    private BeanOne beanOne;
}

2.3 流程图解

其代码步骤,在附录都有。希望在看完这个图之后,能理解流程。注意,请跟着箭头的方向看。

在这里插入图片描述

2.4 构造方法注入在循环依赖时产生的问题。

2.4.1 前置代码准备

  • BeanOne
@Component
public class BeanOne {

    private final BeanTwo beanTwo;

    /**
     * 构造方法注入
     */
    @Autowired
    public BeanOne(BeanTwo beanTwo) {
        this.beanTwo = beanTwo;
    }
}
  • BeanTwo
@Component
public class BeanTwo {

    private final BeanOne beanOne;

    /**
     * 构造方法注入
     */
    @Autowired
    public BeanTwo(BeanOne beanOne) {
        this.beanOne = beanOne;
    }
}

  • 测试代码(好像给这个没啥用,哈哈哈)
@Test
public void test3() {
    AnnotationConfigApplicationContext context =
        new AnnotationConfigApplicationContext(AppConfig.class);
}

2.4.2 结果

​ 那结果必定是创建失败的。

2.4.3 为什么?

​ 为什么会失败呢?那我们查看源码,得知的流程为:如果你这边就一个有参的构造方法,他必然会使用你这个有参的构造方法进行对象的创建。

那么流程来了:

1. 准备创建 beanOne 对象:首先将 beanOne的beanName 放入 标识正在创建的list 变量中(singletonsCurrentlyInCreation) 详情看代码附录 2
2. 获得beanOne的构造方法,因为他就一个有参的,用此构造方法进行对象的创建(我已经通过源码验证)
3. 解析beanOne 的唯一一个有参的构造方法, 发现参数中有一个BeanTwo 类型的变量`,则调用getBean()获取BeanTwo类型的变量注入


4. 到了getBean()方法来获取beanTwo 对象了,首先看singletenObjects 等容器中有没有,肯定是没有,因为还没创建
5. 准备创建beanTwo对象: 首先将beanTwo的beanName 放入 标识正在创建的list 变量中(singletonsCurrentlyInCreation) 详情看代码附录 2
6. 获得beanTwo 的构造方法,因为他就一个有参的,用此构造方法进行对象的创建(我已经通过源码验证)
7. 解析 beanTwo 的唯一一个有参的构造方法,发现参数中有一个BeanOne 类型的变量,则调用getBean()获取BeanOne类型的变量注入

8. 到了getBean() 方法来获取beanOne 对象了,首先看singletenObjects 等容器中有没有,肯定是没有,因为还没创建,我们也放不了。
9. 又准备创建beanOne对象:那么重点来了,首先将 beanOne的beanName 放入 标识正在创建的list 变量中(singletonsCurrentlyInCreation) 详情看代码附录 2
然后,你看代码附录2中的代码可以察觉到,我们this.singletonsCurrentlyInCreation.add(beanName) 这句,肯定放不进去,因为有相同的了,所以此语句返回了false, 然后!取反,就是true。然后与另一个语句进行结合。这条语句的判断,就为true ,直接if语句体,然后直接抛了一个异常,然后就没然后了。创建失败。!!结束。

这就是根本原因。

2.5 在循环依赖状态下 使用 构造方法注入与setter(or @Autowired ) 混合的方式产生的问题

​      那我想说的是,就别抽那个风了。这个跟两个对象实例化的顺序有关联,比如 Bean1是使用的是setter or 字段 @Autowired方式注入的。且使用构造方法,能创建出来对象的,那么一创建完成(属性注入之前) 就放到了三级缓存中。

     那么剩下的bean2无论是使用构造方法注入,还是setter方式注入,都不会影响了,因为在bean2 需要bean1 的时候去调用getBean() 方法。然后在getBean() 方法的一开始,就会去缓存拿,那么由于bean1 已经通过构造方法创建了,放入了缓存中,那么必定可以拿到。拿到之后就直接返回了,然后把返回的bean1 直接注入到bean2 中。(虽然现在的bean1中属性还没有注入完整。)

    那么显然再拿bean1 的时候,根本就直接返回了,不会执行后面复杂的逻辑,也就不会执行到 代码附录2 中的那个方法,也就不会抛出异常了(其他的地方检测抛出的,没在论述范围之内)。


那么反过来,那就出问题了。

  1. bean1 放入 标识正在创建的list 变量中(singletonsCurrentlyInCreation) 详情看代码附录 2
  2. bean1使用的是 构造方法注入,然后调用bean1构造方法的时候,发现需要获得bean2. 那么就调用getBean() 来创建bean2
  3. bean2 放入标识正在创建的list 变量中(singletonsCurrentlyInCreation) 详情看代码附录 2
  4. bean2,调用构造方法创建出来了bean2类的实例,然后再调用populateBean() 方法进行bean1的注入,那么再获取bean1的时候,就调用getBean()方法
  5. getBean() 方法获取bean1, 首先去缓存中找,发现没有。因为步骤1bean1根本正在调用构造方法中,根本连实例都没产生出来。所以肯定没有放入缓存,那么去缓存拿,肯定拿不到。之后又进行Bean1的创建
  6. bean1 又要放入标识正在创建的list 变量中(singletonsCurrentlyInCreation) 详情看代码附录 2
  7. 因为在步骤1 里面已经放了,现在再放,肯定会进入 代码附录2 中的if 体里面,自然会抛出异常。
  8. 没了。

2.5 循环依赖中关于aop代理对象的问题

那么有人就问了:这关aop啥事?

      比如这么一个情况,我有两个bean产生了循环依赖。这两个bean都是配置了aop的类,那么问题来了。

bean1 只是调用构造方法实例化了一下,就扔到了三级缓存中了?

还是在这个期间,调用aopBeanPostProcessor 给我们产生一个代理对象,然后把这个代理对象扔到了三级缓存

那么通过观察源码发现,在此期间并没有应用aop。我也不能在这说啥就是啥吧?那么来通过调试证实一下,证明我不是道听途说的(扔了个lambda(ObjectFactory的实现)可以看到bean现在还没有被代理):

在这里插入图片描述

那么排除了上面的,那么就一种可能,就是在从三级缓存中获取的时候,就会调用这个lambda,然后应用对应的BeanPostProceesor 产生aop代理对象。验证:

在这里插入图片描述

3. 总结

那么上面一堆,问:spring 是如何解决循环依赖的问题的呢?

自己根据理解瞎聊就行。(下面总结一下大概)

通过三级缓存解决了循环依赖。

Bean1Bean2 发生循环依赖的时候呢,比如首先实例化的是Bean1,那么首先是去缓存里面取,那么肯定拿不到,然后调用Bean1的相应的构造方法进行Bean1 的实例化,然后再把Bean1的对象放入三级缓存,然后接下来就是调用populateBean()方法注入Bean1对应的属性,注入属性的时候,发现依赖了Bean2则调用getBean() 方法获取Bean2 的对象。


那么就进入了创建Bean2的流程,首先去缓存中拿Bean2, 肯定拿不到。然后调用Bean2对应的构造方法,创建了Bean2的实例,然后放入三级缓存,之后调用populateBean() 方法注入Bean2对应的属性,然后发现依赖了Bean1。那么就该调用getBean() 方法获取Bean1 了。


那么又进入了创建了Bean1的流程,那么这次还是从缓存中拿,发现有了!!!直接返回这个Bean1

然后Bean2初始化完成,然后返回,(虽然引用的Bean1里面的属性还没有注入完成。)

Bean2返回给Bean1 了,所以Bean1 也注入了Bean2

那么此时Bean2 中引用的Bean1也完整了,


解释一下一个基础的问题😂

因为这个是引用啊,就是Bean2 拿的是Bean1 的地址啊。

那Bean1 的属性更新了, 那就都更新了。(这么基础的问题还是解释一下吧)

3.1 注入方式不同对循环依赖的影响

注入方式结果
两个都是setter/ 字段@Autowired成功
两个都是构造方法注入失败
构造方法与 setter/字段@Autowired 混合先创建的采用setter/字段@Autowired
则可以创建成功,反之不能。

其他的细节就不总结了。

代码附录

1. DefaultSingletonBeanRegistry#getSingleton()


调用位置:

  • doGetBean()->
    • Object sharedInstance = getSingleton(beanName);
      代码:
/**
 * Return the (raw) singleton object registered under the given name.
 * <p>Checks already instantiated singletons and also allows for an early
 * reference to a currently created singleton (resolving a circular reference).
 * @param beanName the name of the bean to look for
 * @param allowEarlyReference whether early references should be created or not
 * @return the registered singleton object, or {@code null} if none found
 */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	Object singletonObject = this.singletonObjects.get(beanName);
	/*isSingletonCurrentlyInCreation(): 根据beanName 判断此对象是否正在创建(是否在创建的list中)*/
	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;
}

2. DefaultSingletonBeanRegistry#beforeSingletonCreation()


调用位置:

  • doGetBean ->

    • sharedInstance = getSingleton(beanName, () -> {

      • /*将beanName 存入 singletonsCurrentlyInCreation(当前正在创建的bean的名称) list 中*/
        beforeSingletonCreation(beanName);
        

代码:

/**
 * Callback before singleton creation.
 * <p>The default implementation register the singleton as currently in creation.
 * @param beanName the name of the singleton about to be created
 * @see #isSingletonCurrentlyInCreation
 */
protected void beforeSingletonCreation(String beanName) {
	/*
	inCreationCheckExclusions: 存储的是 在当前创建检查中 被排除的bean的名称
	singletonsCurrentlyInCreation: 存储的是  当前正在创建的bean的名称
	*/
	if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
		throw new BeanCurrentlyInCreationException(beanName);
	}
}

3. AbstractAutowireCapableBeanFactory#createBeanInstance


调用位置:

  • AbstractAutowireCapableBeanFactory#doCreateBean

    • instanceWrapper = createBeanInstance(beanName, mbd, args);
      

代码:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
	// Make sure bean class is actually resolved at this point.
	Class<?> beanClass = resolveBeanClass(mbd, beanName);

	// ..............上面省略

	/*
	需要去确定一个或者多个构造方法。然后利用这些构造方法返回的构造方法 进行bean的创建。
	此方法点进去可以查看他是如何选定合适的构造方法的。
	*/
	// Need to determine the constructor...
	Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
	if (ctors != null ||
			mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
			mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
		/*使用自动注入方式的构造方法*/
		return autowireConstructor(beanName, mbd, ctors, args);
	}

	/*使用无参的构造方法直接构造返回*/
	// No special handling: simply use no-arg constructor.
	return instantiateBean(beanName, mbd);
}

4. DefaultSingletonBeanRegistry#addSingletonFactory

调用位置:

  • AbstractAutowireCapableBeanFactory#doCreateBean->

    • /*放入singletonFactories中(三级缓存)*/
      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) {
		if (!this.singletonObjects.containsKey(beanName)) {
             /* 加入三级缓存*/
			this.singletonFactories.put(beanName, singletonFactory);
			/* 如果二级缓存中有,则移除*/
             this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

5. AbstractAutowireCapableBeanFactory#doCreateBean

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

	// Initialize the bean instance.
	Object exposedObject = bean;
	try {
         /* 进行属性值的注入*/
		populateBean(beanName, mbd, instanceWrapper);
         /*
		进行bean的初始化:
			应用 BeanPostProcessor(当然也包括aop) , 调用实现了InitializingBean或定义了一个自定义init方法afterPropertiesSet 方法@PostConstruct注解的方法等(生命周期中的方法)
			invokeAwareMethods的调用
		*/
		exposedObject = initializeBean(beanName, exposedObject, mbd);
	}

	//.............
        
}

6. AbstractAutowireCapableBeanFactory#initializeBean()


调用位置 :

  • doCreateBean()->
    • exposedObject = initializeBean(beanName, exposedObject, mbd);

代码:

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
	if (System.getSecurityManager() != null) {
		AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
			invokeAwareMethods(beanName, bean);
			return null;
		}, getAccessControlContext());
	}
	else {
		/*
		调用Aware 的方法
		例如如果你实现了其特定的Aware 接口的话,会在此调用接口中的方法给其传值

			比如你实现了BeanFactoryAware 接口,他在这会调用其中的setBeanFactory() 方法,
			把方法需要的参数:bean工厂传给你。你就可以用了。

		里面的代码非常的简单,点进去看看,就知道了。
		*/
		invokeAwareMethods(beanName, bean);
	}

	Object wrappedBean = bean;
	if (mbd == null || !mbd.isSynthetic()) {
		/* 应用 BeanPostProcessor 的 postProcessorsBeforeInitialization 方法,
				注意@PostConstruct 注解的方法,也是其中一个BeanPostProcessor 处理的*/
		wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	}

	try {
		/*如调用afterPropertiesSet(), 自定义的init 方法,*/
		invokeInitMethods(beanName, wrappedBean, mbd);
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				(mbd != null ? mbd.getResourceDescription() : null),
				beanName, "Invocation of init method failed", ex);
	}
	if (mbd == null || !mbd.isSynthetic()) {
		/*
		应用 BeanPostProcessor 的 processorsAfterInitialization 方法
			如:aop代理对象的生成; 事件发布等;
		*/
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}

	return wrappedBean;
}

7. DefaultSingletonBeanRegistry#addSingleton


调用位置:

  • doGetBean()

    • sharedInstance = getSingleton(beanName, () -> {
      
      • /* 放入singletonObjects 里面(狭义的ioc容器。)*/
        addSingleton(beanName, singletonObject);
        

代码:

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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值