Spring框架如何解决循环依赖

一、什么是循环依赖?

从字面理解上,就是对象A依赖对象B的同时,对象B也依赖上了对象A。

具体来讲,指的是Bean与Bean之间的依赖关系,循环依赖指的是两个或者多个Bean相互依赖。

简单来讲,如下图关系所示:

5dd9f23f727f47278ddc34a8d9282532.png

 代码显示:(注解形式)

一、创建dao(数据访问层)

1.1    接口代码
public interface IAccountDao {
   
    public void addAcc(Account account);

}

1.2    接口实现类代码
@Repository
public class AccountDaoImp implements IAccountDao {

//    DBUtil提供核心类QueryRunner
//    QueryRunner提供增删改 update();查询query();

    //    设置成员变量
    @Autowired
    private QueryRunner queryRunner;

    //      构造QueryRunner set方法
    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    //  新增方法
    public void addAcc(Account account) {
        try {
            queryRunner.update("insert into account(aname,amoney) value (?,?)", account.getAname(), account.getAmoney());
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

============================ 分割线 ===============================

二、创建service(业务层)

2.1    接口代码
public interface IAccountService {
    //    增删改查方法
    public void addAcc(Account accoount);
}

2.2    接口实现类代码

@Service
public class AccountServiceImp implements IAccountService {
   
     @Autowired//注入bean

    private IAccountDao dao;
//    set方法

    public void setDao(IAccountDao dao) {
        this.dao = dao;
    }
}

============================ 分割线 ===============================

三、创建controller(控制器)

3.1    接口代码
public interface IAccountController {

    public void addAcc(Account account);

}

3.2    接口实现类代码

@Controller
public class AccountControllerImp implements IAccountController {

    //设置属性
    @Autowired
    private IAccountService service;

//    set方法
    public void setService(IAccountService service) {
        this.service = service;
    }
}

当然,上述三层结构是最常见的一种循环依赖,比较特殊的还有:“自己依赖自己”

// 自己依赖自己
@Component
public class A {
    // A中注入了A
 @Autowired
 private A a;
}

配置文件(格式)

<bean> 
<!--======================bean的自动装配==========================-->
    <!--注入dao-->
    <bean id="b" class="com.apesource.dao.AccountDaoImp">
    </bean>


    <!--注入service-->
    <bean id="a" class="com.apesource.service.AccountServiceImp">
    </bean>


    <!--注入controller-->
    <bean id="controllerImp" class="com.apesource.controller.AccountControllerImp">
    </bean>

</beans>

二、什么情况下循环依赖可以被处理?

使用前提:

循环依赖包括: 1、构造器注入循环依赖 set , 注入循环依赖 和 prototype模式Bean的循环依赖。

2、出现循环依赖的Bean必须要是单例

三、Spring是如何解决的循环依赖?

嵌入代码:

@Component
public class A {
    // A中注入了B
 @Autowired
 private B b;
}

@Component
public class B {
    // B中也注入了A
 @Autowired
 private A a;
}

 Spring在创建Bean的过程中分为三步

  1. 实例化,对应方法:AbstractAutowireCapableBeanFactory中的createBeanInstance方法

  2. 属性注入,对应方法:AbstractAutowireCapableBeanFactorypopulateBean方法

  3. 初始化,对应方法:AbstractAutowireCapableBeanFactoryinitializeBean

Spring在创建Bean的时候默认是按照自然排序来进行创建的,所以第一步Spring会去创建实体类A,        具体是

  1. 实例化,简单理解就是new了一个对象

  2. 属性注入,为实例化中new出来的对象填充属性

  3. 初始化,执行aware接口中的方法,初始化方法,完成AOP代理

创建实体类A的过程实际上就是调用getBean()方法,这个方法有两层含义

  1. 创建一个新的Bean

  2. 从缓存中获取到已经被创建的对象

eaa34242595144b795d7a8568234e1c8.png 调用getSingleton(beanName)

首先调用getSingleton(a)方法,这个方法又会调用getSingleton(beanName, true)

getSingleton(beanName, true)这个方法实际上就是到缓存中尝试去获取Bean,整个缓存分为三级

  1. singletonObjects,一级缓存,存储的是所有创建好了的单例Bean

  2. earlySingletonObjects,完成实例化,但是还未进行属性注入及初始化的对象

  3. singletonFactories,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象

因为A是第一次被创建,所以不管哪个缓存中必然都是没有的,注入就发生在第二步,属性赋值;对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,结合这个过程,Spring为了解决单例的循环依赖问题,使用了三级缓存。

Spring对循环依赖的解决方法可以概括为 用三级缓存方式达到Bean提前曝光的目的

/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
 
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
 
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

这三级缓存分别指:

1、一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例
2、二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例
3、三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
	...
	@Override
	@Nullable
	public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
	}
	@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);
				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;
	}
	...
	public boolean isSingletonCurrentlyInCreation(String beanName) {
		return this.singletonsCurrentlyInCreation.contains(beanName);
	}
	protected boolean isActuallyInCreation(String beanName) {
		return isSingletonCurrentlyInCreation(beanName);
	}
	...
}

拿bean的时候先从一级缓存singletonObjects中获取;
1、 如果获取不到或者对象正在创建中,就从二级缓存earlySingletonObjects中获取;
2、 如果还是获取不到就从三级缓存singletonFactories中获取,然后将获取到的对象放到二级缓存earlySingletonObjects中,并且将bean对应的三级缓存singletonFactories清除;

3、 bean初始化完毕,放到一级缓存singletonObjects中,将bean对应的二级缓存earlySingletonObjects清除

总结:

Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池singletonObjects),二级缓存为早期曝光对象earlySingletonObjects三级缓存为早期曝光对象工厂singletonFactories)。

当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。

当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取,第一步,先获取到三级缓存中的工厂;第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

===========注释=============

因本人能力有限,本文章部分内容参考于【程序员DMZ 】作者的《讲一讲Spring中的循环依赖》一文。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值