bean循环依赖的解决
- 前言
- 一、循环依赖是什么?
- 二、循环依赖处理机制
- prototype 原型 bean循环依赖(⽆法解决)
- 单例 bean set注入循环依赖解决过程
- 测试代码
- 开始初始化TestBean
- 注入TestBean属性时需要 初始化itBean
- itBean初始化完成
- 完成TestBean初始化 放入单例池
- 总结
- 文章源码地址
前言
spring的ioc容器帮助我们解决了复杂对象之前的创建问题,我们需要什么对象,只要是被spring管理的bean都可以直接向spring获取改对象,但是当A对象里面的属性是B对象 ,而B对象里面包含属性A对象,这种情况就叫做循环依赖,spring是怎么处理的呢?本文就一起来探讨一下spring是如何解决循环依赖的,以及能解决什么类型的循环依赖。
一、循环依赖是什么?
循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A
依赖于B,B依赖于C,C⼜依赖于A。是对象的相互依赖关系。跟循环调用不同,循环调⽤其实就是⼀个死循环,除⾮有终结条件
在spring中bean会先实例化,然后再注入属性,注入属性大致分两种
- 构造函数注入
- set方法注入
其中,构造器的循环依赖问题⽆法解决,只能拋出 BeanCurrentlyInCreationException 异常,在解决
属性循环依赖时,spring采⽤的是提前暴露对象的⽅法。也就是常说的采用三级缓存!
二、循环依赖处理机制
在Spring中只有设置为单例的通过setXxx或者 @Autowired⽅法才能解决循环依赖。其他的都会抛出BeanCurrentlyInCreationException异常。
prototype 原型 bean循环依赖(⽆法解决)
AbstractBeanFactory.doGetBean()⽅法:
// 如果是 Prototype 并开启循环依赖 报错
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>)
curVal).contains(beanName))));
}
在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进⾏标记
这个beanName正在被创建,等创建结束之后会删除标记
try {
//创建原型bean之前添加标记
beforePrototypeCreation(beanName);
//创建原型bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
//创建原型bean之后删除标记
afterPrototypeCreation(beanName);
}
单例 bean set注入循环依赖解决过程
流程图
Spring容器初始化ClassA通过构造器初始化对象后提前暴露到Spring容器。
通过xml注入TestBean和Itbean来分析循环依赖的调用过程
测试代码
IocTest
/**
* Ioc 容器源码分析基础案例
*/
@Test
public void testIoC() {
// ApplicationContext是容器的高级接口,BeanFacotry(顶级容器/根容器,规范了/定义了容器的基础行为)
// Spring应用上下文,官方称之为 IoC容器(错误的认识:容器就是map而已;准确来说,map是ioc容器的一个成员,
// 叫做单例池, singletonObjects,容器是一组组件和过程的集合,包括BeanFactory、单例池、BeanPostProcessor等以及之间的协作流程)
/**
* Ioc容器创建管理Bean对象的,Spring Bean是有生命周期的
* 构造器执行、初始化方法执行、Bean后置处理器的before/after方法、:AbstractApplicationContext#refresh#finishBeanFactoryInitialization
* Bean工厂后置处理器初始化、方法执行:AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors
* Bean后置处理器初始化:AbstractApplicationContext#refresh#registerBeanPostProcessors
*/
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
TestBean testBean = applicationContext.getBean(TestBean.class);
System.out.println(testBean);
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--循环依赖问题-->
<bean id="testBean" class="com.my.test.TestBean">
<property name="ItBean" ref="itBean"/>
<property name="name" value="aa"/>
</bean>
<bean id="itBean" class="com.my.test.ItBean">
<property name="TestBean" ref="testBean"/>
</bean>
<!-- <bean id="myBeanFactoryPostProcessor" class="MyBeanFactoryPostProcessor"/>-->
<!-- <bean id="myBeanPostProcessor" class="MyBeanPostProcessor"/>-->
<!--<bean id="testBean" class="TestBean">
</bean>-->
<!-- <!–aop配置–>-->
<!-- <!–横切逻辑–>-->
<!-- <bean id="logUtils" class="LogUtils">-->
<!-- </bean>-->
<!-- <aop:config>-->
<!-- <aop:aspect ref="logUtils">-->
<!-- <aop:before method="beforeMethod" pointcut="execution(public void TestBean.print())"/>-->
<!-- </aop:aspect>-->
<!-- </aop:config>-->
</beans>
TestBean
package com.my.test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* @Author
* @create 2019/12/3 11:46
*/
public class TestBean implements InitializingBean, ApplicationContextAware {
private ItBean itBean;
private String name;
public void setItBean(ItBean itBean) {
this.itBean = itBean;
}
public void setName(String name) {
this.name = name;
}
/**
* 构造函数
*/
public TestBean(){
System.out.println("TestBean 构造器...");
}
/**
* InitializingBean 接口实现
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("TestBean afterPropertiesSet...");
}
public void print() {
System.out.println("print方法业务逻辑执行");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("setApplicationContext....");
}
}
ItBean
package com.my.test;
/**
* @author
*/
public class ItBean {
private TestBean testBean;
public void setTestBean(TestBean testBean) {
this.testBean = testBean;
}
/**
* 构造函数
*/
public ItBean(){
System.out.println("ItBean 构造器...");
}
}
开始初始化TestBean
调用AbstractBeanFactory.doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)
// 从ioc容器中获取 如果为空则是该类第一次初始化
Object sharedInstance = getSingleton(beanName);
DefaultSingletonBeanRegistry.getSingleton(String beanName, boolean allowEarlyReference)
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
// 先从一级缓存单例池中获取
Object singletonObject = this.singletonObjects.get(beanName);
// 单例池中还没有 并且当前类正在初始化
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 一级缓存没有 从二级缓存中获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 二级缓存没有 加锁三级缓存对象
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
// 从一级缓存中拿
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 一级缓存中没有 从二级缓存中拿
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
// 二级缓存没有 从三级缓存中拿
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 三级缓存有 将对象从三级缓存移除 添加到到二级缓存中
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
创建之前先调用DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory<?> singletonFactory ) 看一下是否在单例池中是否该对象 没有在创建
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 正在被创建 因为spring bean 创建过程复杂 步骤多 需要标识
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 传进来的调用 lamda表达式使用
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;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
然后调用AbstractAutowireCapableBeanFactory.createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)开始创建
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
// 拿到RootBeanDefinition
RootBeanDefinition mbdToUse = mbd;
// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
// 获取到类的信息
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
//真正创建bean的方法
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
将实例化好的TestBean放入三级缓存中
// 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));
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
// 当前bean不在一级缓存
if (!this.singletonObjects.containsKey(beanName)) {
// 添加到三级缓存
this.singletonFactories.put(beanName, singletonFactory);
// 删除二级缓存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
注入TestBean属性时需要 初始化itBean
开始注入属性 itBean 创建bean 回到调用AbstractBeanFactory.doGetBean
AbstractBeanFactory.doGetBean(String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)
C.将实例化好的 ItBean 放入三级缓存中
**D. ItBean 注入属性类TestBean 又回到调用AbstractBeanFactory.doGetBean **
从DefaultSingletonBeanRegistry.getSingleton(String beanName, boolean allowEarlyReference) 方法三级缓存中拿到TestBean 并将其从三缓存中移除 放入二级缓存中 这个时候ItBean 还在三级缓存哦
ItBean已经注入好属性TsetBean
itBean初始化完成
调用DefaultSingletonBeanRegistry.addSingleton将ItBean放入一级缓存中
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 一级缓存添加
this.singletonObjects.put(beanName, singletonObject);
// 三级缓存删除
this.singletonFactories.remove(beanName);
// 二级缓存删除
this.earlySingletonObjects.remove(beanName);
// 注册单例名称set集合
this.registeredSingletons.add(beanName);
}
}
完成TestBean初始化 放入单例池
总结
spring ioc容器对单例非懒加载的bean进行初始化时都会先将bean实例化后放入三级缓存singletonFactories中,然后进行属性注入。属性注入能直接注入,就会放入一级缓存singletonObjects,如果注入的是类,则会立即初始化需要注入的类。当需要注入的类实例化后,会先将实例化的类放入三级缓存中,再将最开始的类从三级缓存中移除到二级缓存。当类的所有属性都注入后就会将其添加到单例池中,并从三级和二级缓存中删除。