Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

Stella981
• 阅读 1076

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

Spring循环依赖

什么是Spring的循环依赖?循环依赖会存在哪些问题?

示例:AService依赖BService; BService依赖AService

@Service public class AService { // @Autowired public BService bService; }

@Service public class BService { @Autowired public AService aService; }

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

上面演示的例子就是循环注入

如果改为多例的化,运行时就会报错,循环引用异常,找不到对象

@Scope("prototype")

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

Spring中的循环依赖问题在单例的情况下,Spring是已经帮我们解决好了,多例没有解决循环依赖问题。

为啥,多例的情况下 Spring没有去解决循环依赖问题?

因为在多例的情况下,设置的多例的对象没有明确哪一个,就会产生循环依赖问题。

Spring多例我们要:如何解决循环依赖问题?

我们可以自己去:明确指定引用那个对象

@Service @Scope("prototype") public class AService { // @Autowired public BService bService; // 为什么Aservice在创建的时候 为什么Bservice比ASERVICE 先创建 public AService() { System.out.println("AService被Java的反射技术创建"); } public void setbService(BService bService) { this.bService = bService; }

}

@Service @Scope("prototype") public class BService { // @Autowired public AService aService;

public void setaService(AService aService) {
    this.aService = aService;
}

}

public class SpringApp { public static void main(String[] args) { // 1. ioc容器在创建的时候所有的单例对象是不是会被创建 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class); // // 对例子情况 当你在调用的时候才获取 AService aSerivce = applicationContext.getBean("AService", AService.class); BService bSerivce = applicationContext.getBean("BService", BService.class); aSerivce.setbService(bSerivce); bSerivce.setaService(aSerivce); // 循环引用异常 找不到对象 /** * 思考问题? 如果我们的项目对象必须要是多例? 而且必须要循环引用 明确的指定引用那个对象 */ String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (int i = 0; i < beanDefinitionNames.length; i++) { System.out.println(beanDefinitionNames[i]); }

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

SpringBean循环依赖三级缓存概念

思考问题:单例对象在什么时候创建?

在IOC容器被创建的时候创建

多例的情况下,是在getbean()调用的情况下创建。多例对象每次用完就会去销毁掉。

SpringBean Aservice对象被创建流程步骤源码分析

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

// Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory);

// Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons();

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

public Object getBean(String name) throws BeansException { return this.doGetBean(name, (Class)null, (Object[])null, false); }

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

public Object getSingleton(String beanName) { return this.getSingleton(beanName, true); }

获取缓存对象:

protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName);    //根据BeanName去集合中查,查到就返回这个对象,【】【】【】一级缓存对象集合【】【】【】缓存完整对象【】【】完整对象表示对象已经创建完了,并且对象属性已经赋值了。 if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { Map var4 = this.singletonObjects; synchronized(this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName);    //查询二级缓存中是否有缓存对象 if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);    //查询三级缓存,三级缓存中有的化,将三级缓存中的数据放入二级缓存中 if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

一级缓存没有找到,就去找二级缓存中找

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

singletonObject == null && this.isSingletonCurrentlyInCreation(beanName) //一级缓存没有,并且singletonsCurrentlyInCreation判断是否之前标记为该对象开始创建

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

if (isPrototypeCurrentlyInCreation(beanName)) { //我们对象是单例的,所有不进入 throw new BeanCurrentlyInCreationException(beanName); }

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

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 + "'"); } beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { 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; } }

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }

标识为该对象开始创建

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

最终调用

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

正真去创建我们的Bean对象:

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

既然要创建对象,先反射走无参构造函数,对象先实例化完成,在赋值

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

执行这个方法,输出构造函数打印的语句,说明底层通过Java反射机制初始化的

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

在这之前,我们的对象属于婴儿对象,因为它的属性还没有赋值。都是称为婴儿对象。

那么什么时候赋值呢?

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

populateBean(beanName, mbd, instanceWrapper);//这里给对象属性赋值

在给对象属性赋值之前:

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

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); } } }

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

A对象坚持依赖B对象,这时候B对象也需要被创建

A对象已经存放在三级缓存中,这时候要去创建B对象

此时B对象也要走A对象流程

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

看下调用链

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

也将B对象放入三级缓存

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

总结下:

AService在创建的时候,提前曝光存放到三级缓存中,AService发现依赖BService,这时候Bservice提前曝光存放到三级缓存中去。

此时BService又依赖AService,此时BService经过赋值是完整对象,但是Aservice还是婴儿对象,没有完全创建完毕。

就会去把BService对象注册到一级缓存中,同时会把之前缓存BService对象的二级缓存清除掉

AService对象依赖BService,BService此时已经创建成功了,那么AService在设置属性后,就直接把BService赋值给AService。

开始注册AService对象

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

Spring5.0源码深度解析之SpringBean循环依赖问题解决方案

总结:

SpringBean中 Aservic对象被创建流程步骤源码分析:

  1. doGetBean创建我们bean对象
  2. getSingleton (beanName) 获取缓存对象

注意:完整对象概念:对象已经实例化成功并且所有属性都已经赋值

  • singletonObjects 一级缓存完整对象
  • earlySingletonObjects 二级缓存 缓存婴儿对象
  • singletonFactories 三级缓存存放婴儿对象

理解概念:

  1. 完整对象表示该对象实例化完成并且所有的属性已经赋值。
  2. 婴儿对象(提前对象)对象已经实例化完成但是属性没有赋值的。

singletonObject ==null&&this.singletonsCurrentlyInCreation.contains(beanName);

{

才能够查询二级缓存

}
singletonsCurrentlyInCreation :标记为该对象开始创建

  1. getSingleton(String beanName, ObjectFactory<?> singletonFactory)

this.singletonsCurrentlyInCreation.add(beanName) 表示该对象已经开始创建

  1. createBean() →doCreateBean
  2. addSingletonFactory  将婴儿对象(不完整对象也就是只是实例化完成但是属性没有赋值的) 存放三级缓存中。
  3. A对象已经存放到三级缓存中,开始给对象属性赋值的时候 需要创建B对象。

A对象检查发现依赖B对象 这时候B对象也需要被创建

本文参考:

蚂蚁课堂

http://www.mayikt.com/

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Easter79 Easter79
3年前
springboot源码分析
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Stella981 Stella981
3年前
Spring5.0源码深度解析之理解Configuration注解
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Stella981 Stella981
3年前
Spring5.0源码深度解析之SpringBean的Aop的使用
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Stella981 Stella981
3年前
Spring5.0源码深度解析之Spring核心注解
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Stella981 Stella981
3年前
Spring5.0源码深度解析之SpringBean的Aop源码分析
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)SpringAop源码分析
Stella981 Stella981
3年前
Spring5.0源码深度解析之SpringBean声明事务底层实现原理
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Stella981 Stella981
3年前
Spring5.0源码深度解析之SpringBean的生命周期
!(https://gss1.bdstatic.com/vo3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign0c730b84bd19ebc4c078719fba1da8c1/37d12f2eb9389b503a80d4b38b35e5dde6116ed7.jpg)
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这