Spring源码六:Bean的实例化-循环依赖


Bean的实例化-循环依赖

循环依赖

如果存在A类中有B属性,B类中有A属性,那么当依赖注入的时候,就会产生当A类还没创建完成的时候,由于对B类的创建再次返回创建A类,最终造成循环依赖。
  • Spring 中的循环依赖只会出现在单例实例无参构造函数实例化情况下。
  • 单例模式有参构造器或者多例模式,Spring都无法解决循环依赖问题,都会直接报错。

循环依赖的实例

类:CircularRelyA:

@Component
public class CircularRelyA {

    @Autowired
    private CircularRelyB mCircularRelyB;
}

类:CircularRelyB:

@Component
public class CircularRelyB {

    @Autowired
    private CircularRelyA mCircularRelyA;
}

循环依赖的相关方法

单例模式下的循环依赖流程

├─ getBean ① 实例化入口
│ ├─ doGetBean
│ │ ├─ getSingleton ② 查看缓存里是否有实例(本节重点方法)
│ │ ├─ getObjectForBeanInstance ③ 如果缓存有实例,直接返回实例
│ │ ├─ getSingleton ④ 缓存没有实例,创建实例(本节重点方法)
│ │ │ ├─ beforeSingletonCreation ⑤ 添加beanName到正在实例化的bean的set集,如果set中已存在当前beanName,则报错
│ │ │ ├─ createBean
│ │ │ │ ├─ doCreateBean ⑥ 执行创建实例
│ │ │ │ │ ├─ createBeanInstance ⑦ 创建实例过程
│ │ │ │ │ │ ├─ determineConstructorsFromBeanPostProcessors ⑧ 如果构造函数有@Autowired
│ │ │ │ │ │ │ ├─ autowireConstructor ⑨ 执行构造器实例过程 不会走到步骤 ⑪
│ │ │ │ │ │ │ │ └─ getBean 返回 ① 实例化入口
│ │ │ │ │ ├─ applyMergedBeanDefinitionPostProcessors ⑩ 注解的搜集和装配过程
│ │ │ │ │ ├─ earlySingletonExposure ⑪ 暴露还没有完全实例化完成的 bean
│ │ │ │ │ ├─ addSingletonFactory ⑫ 将提前暴露的 bean,放到三级缓存(本节重点方法)
│ │ │ │ │ ├─ populateBean ⑬ 注依赖注入的核心过程
│ │ │ │ │ │ ├─ inject
│ │ │ │ │ │ │ ├─ AutowiredFieldElement.inject ⑭ 有@Autowired注解域的注入
│ │ │ │ │ │ │ │ └─ getBean 返回①实例化入口
│ │ │ │ │ │ │ ├─ AutowiredMethodElement.inject ⑭ 有@Autowired注解方法的注入
│ │ │ │ │ │ │ │ └─ getBean 返回①实例化入口
│ │ │ │ │ ├─ initializeBean ⑮ bean 实例化和IOC依赖注入完以后的增强处理过程
│ │ │ │ │ └─ registerDisposableBeanIfNecessary ⑮ bean 的销毁过程
│ │ │ ├─ afterSingletonCreation ⑯ 实例化完成,将beanName从正在实例化的bean的set集合中删除
│ │ │ └─ addSingleton ⑰ 创建完成后,将完全实例化后的bean 放到一级缓存


循环依赖的步骤说明

CircularRelyA简称为 A,CircularRelyB简称为B,循环依赖步骤如下:

  • 1、A类首先从① 实例化入口进来,到⑤ 添加beanName到正在实例化的bean的set集合,再执行到⑦ 创建实例后,执行⑫设置三级缓存
  • 2、A类 继续执行⑬ populateBean进行依赖注入,这里触发了 B类属性的 getBean操作,重新返回① 实例化入口
  • 3、B类,到⑤ 添加beanName到正在实例化的bean的set集合,再执行执行到⑦ 创建实例后,执行⑫ 设置三级缓存
  • 4、B类继续执行⑬ populateBean进行依赖注入,这里触发了 A类属性的 getBean操作,重新返回① 实例化入口
  • 5、A类 执行到②从三级缓存中拿到的提前暴露的A实例,该实例还没有进行 B类属性的依赖注入的,B类属性为空。
  • 6、B类拿到了 A提前暴露的实例后,顺利的执行完⑬ 依赖注入,执行⑰创建完成后,将完全实例化后的bean⑰ 放到一级缓存,至此B类的实例化已经完全做完;
  • 7、由于B类的实例化是由 A类实例化中 B属性的依赖注入触发的 getBean操作进行的,现在 B已经实例化,所以 A类中 B属性就可以完成⑬ populateBean操作,这时候 A的 B属性已经有值;
  • 8、B类中的 A属性指向的就是 A类实例堆空间,所以这时候 B类中 A属性也会有值;
  • 9、此时A类顺利的得到了B属性的注入值,也完成了自己的初始化流程,也会将实例化后的bean ⑰ 放到一级缓存
  • 10、循环依赖正式结束。

循环依赖中重点方法

getSingleton ()方法② 查看缓存里是否有实例

类文件:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

	@Nullable
	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);
				//如果还拿不到,并且允许bean提前暴露
				if (singletonObject == null && allowEarlyReference) {
					// 二级缓存没有, 并且可以提前暴露的话, 则从三级缓存中获取ObjectFactory
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					// 如果三级缓存不为空
					if (singletonFactory != null) {
						// 调用的是AbstractAutowireCapableBeanFactory的getEarlyBeanReference方法
						singletonObject = singletonFactory.getObject();
						// 将对象放到二级缓存中
						this.earlySingletonObjects.put(beanName, singletonObject);
						// 删除三级缓存数据
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

getSingleton()方法④ 缓存没有实例,创建实例

类文件:org.springframework.beans.factory.support.DefaultSingletonBeanRegistry

	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) {
					throw new BeanCreationNotAllowedException(beanName,
							"Singleton bean creation not allowed while singletons of this factory are in destruction " +
							"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
				}
				// 如果要创建的bean在singletonsCurrentlyInCreation 中存在则报错
				// 因为在这里的bean都未实例化完成
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					// 调用singletonFactory中的方法, 如果得到singletonObject,则表示已经彻底完成创建
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						throw ex;
					}
				}
				catch (BeanCreationException ex) {
					if (recordSuppressedExceptions) {
						for (Exception suppressedException : this.suppressedExceptions) {
							ex.addRelatedCause(suppressedException);
						}
					}
					throw ex;
				}
				finally {
					if (recordSuppressedExceptions) {
						this.suppressedExceptions = null;
					}
					// bean创建完成后,从singletonsCurrentlyInCreation删除该bean
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					// 清除二级和三级缓存, 将Bean对象存到一级缓存中
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

有参数构造函数和多例循环依赖


有参构造函数循环依赖(报错失败)

有参构造函数循环依赖

  • A类从① 实例化入口进来,到⑤ 添加beanName到正在实例化的bean的set集合,再执行到⑦ 创建实例
  • 2、通过⑨ 执行构造器实例过程,进行B类的getBean操作,并没有⑫ 设置三级缓存;
  • 3、导致B类⑨ 执行构造器实例过程,进行A类的getBean操作,执行到② 缓存中没有实例,最后执行到 ⑤ 添加beanName到正在实例化的bean的set集时,set中已存在当前beanName,则报错

多例模式循环依赖(报错失败)

和有参构造的思想比较相似,直接上doGetBean方法源码,可以看注释

类文件:org.springframework.beans.factory.support.AbstractBeanFactory

	@SuppressWarnings("unchecked")
	protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

			// 省略多例无关代码
			
			// 多例模式, 查看prototypesCurrentlyInCreation中是否有当前beanName
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// 省略多例无关代码

			// 判断是否是多例
			else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						//  将要创建的bean添加在prototypesCurrentlyInCreation 中
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					// 该方法是FactoryBean接口的调用入口
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}
			
			// 省略多例无关代码
	}


循环依赖的流程图(Jack老师绘制)


循环依赖的补充说明

Bean的销毁

首先我们来看一下⑯ Bean的销毁过程
进入 doCreateBean ()方法
类文件:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

try {
	... 
	
	//注册bean销毁时的类DisposableBeanAdapter
	registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
	throw new BeanCreationException(
		mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}

进入 registerDisposableBeanIfNecessary ()方法

类文件:org.springframework.beans.factory.support.AbstractBeanFactory

	protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
		AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
		if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
			// 单例
			if (mbd.isSingleton()) {
				// 对该bean注册一个销毁的DisposableBeanAdapter对象,对给定的bean执行销毁工作 (@Destroy)
				//  DestructionAwareBeanPostProcessor 类型的接口
				registerDisposableBean(beanName,
						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
			}
			else {
				// A bean with a custom scope...
				Scope scope = this.scopes.get(mbd.getScope());
				if (scope == null) {
					throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
				}
				scope.registerDestructionCallback(beanName,
						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
			}
		}
	}


Bean的销毁如何被触发

1、在Spring 中执行显式关闭上下文,关闭时候会执行实现DisposableBean接口类中的destroy() 方法

    @Test
    public void testSpringStart() {
        ConfigurableApplicationContext wApplicationContext = new ClassPathXmlApplicationContext("spring.xml");
        // 以下两种显式关闭都会调用实现了DisposableBean接口类中的destroy() 方法
        // 1、显式关闭上下文
        wApplicationContext.close();
        // 2、显式关闭上下文
        wApplicationContext.registerShutdownHook();
    }

2、在Web环境中,当Tomcat关闭的时候就会调用到 servlet中的销毁方法,在这个方法中就会最终也会掉用到Spring中 DisposableBeanAdapter类的destroy()方法,该方法就会根据前面的收集进行调用。

servlet中的销毁方法

    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }

    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }

    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }
	//  servlet中的销毁方法
    public void contextDestroyed(ServletContextEvent event) {
       // 该方法最终会调用Spring的DisposableBeanAdapter类的destroy()
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值