Spring的循环依赖,循环依赖底层原理、三级缓存解决循环依赖源码分析

什么是循环依赖

很简单就是A对象依赖了B对象,B对象依赖了A对象

//A依赖了B

class A{
 public B b;
}

//B依赖了A

class B{
 public A a;
}

如果不考虑Spring,循环依赖并不是问题,因为对象之间相互依赖是很正常的事情

A a=new A();

B b=new B();

a.b=b;

b.a=a;

这样A,B就依赖上了

spring的循环依赖

@Component("aService")
public class AService {
  @Autowired
    private BService bService;
  
  public void test(){
      System.out.println(bService);
  }
}


@Component("bService")
public class BService {
  @Autowired
    private AService aService;
}

 AService的生命周期

1. 实例化(new AService()),原始对象

2.填充bService属性----》从单例池中找---》找不到---》创建BService的Bean

        BService的生命周期

        2.1 实例化(new BService()),原始对象

        2.2 .填充bService属性----》从单例池中找---》找不到---》创建AService的Bean

        2.3 填充其他属性

        2.4 做其他重要的事情

        2.5 放入单例池 Bean对象

3.填充其他属性

4.做其他重要的事情

5. 放入单例池 Bean对象

这就造成了循环依赖

解决方法:我们可以定义一个map,来存储实例化对象,在1的时候存储实例化对象<"aService",AService原始对象>

在2.2的时候,一级缓存单例池找不到,就去找这个map里的对象,进行属性的填充!

 AService的生命周期

1. 实例化(new AService()),原始对象 map存储对象<"aService",AService原始对象>

2.填充bService属性----》从单例池中找---》找不到---》创建BService的Bean

        BService的生命周期

        2.1 实例化(new BService()),原始对象 map存储对象<"bService",BService原始对象>

        2.2 .填充bService属性----》从单例池中找---》找不到---》map---》AService原始对象

        2.3 填充其他属性

        2.4 做其他重要的事情

        2.5 放入单例池 Bean对象

3.填充其他属性

4.做其他重要的事情(Aop)

5. 放入单例池 Bean对象

大致流程 

若第四步需要做Aop,那么就会产生一个Aservice的代理对象,我们放到单例池中去应该是代理对象,我们赋值给BService中的属性AService也应该是代理对象!!!那么问题也随之而来了,在执行流程中,我们属性赋值赋值的是原始对象!!!如何解决?那也就是说我们要提前去做AOP,我们可以在第一步实例化就去创建一个AService代理对象,map里面也存放代理对象。当然这样的话第四步就要去判断AService有没有进行过AOP,若进行过AOP,则第四步就直接跳过。

为什么要提前新建Aop?出现循环依赖 

那么我们在2.2的时候,就需要去判断此对象是否存在循环依赖(也就是AService是否正在创建中),若存在,且满足切面,那么就需要提前进行Aop!!!

如何判断aService正在创建中?

我们可以提前建立一个集合,存放aService,然后在2.2中判断。

若是的话,直接就生成代理对象填充属性

这个aService等创建Bean再释放。

这样问题看似得到了解决!!!

新建CService

@Component("cService")
public class CService {
    @Autowired
    private AService aService;

    public void xxx(){
        aService.test();
    }
}

修改AService

@Component("aService")
public class AService {
  @Autowired
    private BService bService;

  @Autowired
  private CService cService;

  public void test(){
      System.out.println(bService);
  }
}

这样的话,我们填充完BService属性就要填充CService,spring中找不到CService,就要去实例化CService,

 这样就会产生两个AService,但是实际上我们想要B、CService中产生的同一个AService!!!

要解决这个问题很简单,我们只需要将第一个产生的Aservice代理对象放入二级缓存earlySingleObjects里面,填充cService的时候,先从单例池从找,找不到就去二级缓存中找,这样就有效的解决了单例Bean的问题。这就为为什么需要二级缓存的原因,保证单例

二级缓存还有一个作用就是可以在4.5的时候从二级缓存中拿出aService放入单例池中

缓存介绍:

二级缓存 earlySingleObjects   Map<beanName,对象>

一级缓存 单例池 singletonObjects  Map<beanName,bean对象>

三级缓存 singletonFactories Map<String,ObjectFactory<?>>

AService原始对象和AService代理对象的联系

修改下代码

AService

 BService

现在A、BService就没有出现循环依赖了 

主启动类(打断点,像下图那样)

 发现得到的代理对象aService的属性是空的

 下图是原始对象(里面的bService是有值的)

 在打个断点

 

 启动spring

此时的原始对象bean aService是空的

然后会去执行三级缓存的操作(后面有讲) 

 exposedObject对象是我们当前要返回的对象

这个方法是来填充Bean属性的,上图是还没执行此方法的情况

执行完此方法 情况:

 目前为止还未进行AOP

下面此方法就进行aop

 exposedObject就是代理对象,对bService没有进行任何操作

返回的话就是代理对象

 从上面可以看出,进行aop产生代理对象,用到了我们的原始对象

实例化产生的原始对象其实就是放到三级缓存中去的,并且将BeanName和BeanDefinition也一起放进去,大概就是map<'aService',lamda(()->getEarlyBeanReference(beanName,mbd,bean))>

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

大致流程图:

2.2填充属性aService 调用的就是这个方法

@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	            //先从单例池中找
		Object singletonObject = this.singletonObjects.get(beanName);
        //判断是否循环依赖
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            //从二级缓存中找
			singletonObject = this.earlySingletonObjects.get(beanName);
            //二级缓存中也没有
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
				           //从三级缓存中拿
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);  //执行lambla ,也就是执行AOP
							if (singletonFactory != null) {
								singletonObject = singletonFactory.getObject();
								this.earlySingletonObjects.put(beanName, singletonObject); //放到二级缓存
								this.singletonFactories.remove(beanName); 
//从三级缓存中移除
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

执行了aop,就从三级缓存中移除,是因为要防止再次生产对象(防止二次代理)!!!

类似的再添加表达式去三级缓存,也有类似现象

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

 2.2中去三级缓存找后,不一定会生产代理对象(不一定走Aop),也许还是原始对象,放入二级缓存中

判断是否出现循环依赖对应饿源码

 //AService出现了循环依赖
 public Object getEarlyBeanReference(Object bean, String beanName) {
        Object cacheKey = this.getCacheKey(bean.getClass(), beanName); //beanName aService
             //判断      
         this.earlyProxyReferences.put(cacheKey, bean);
        return this.wrapIfNecessary(bean, beanName, cacheKey); //代理对象
    }


protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        } else if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        } else if (!this.isInfrastructureClass(bean.getClass()) && !this.shouldSkip(bean.getClass(), beanName)) {
            //获取当前beanClass所匹配的advisors,判断有误切面
            Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
        //如果匹配的advisors不等于null,name则进行代理,并返回代理对象
            if (specificInterceptors != DO_NOT_PROXY) {
                this.advisedBeans.put(cacheKey, Boolean.TRUE);
            //基于bean对象和Advisor创建代理对象
                Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      //存一个代理对象的类型            
    this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            } else {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return bean;  //返回原始对象
            }
        } else {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;  //返回原始对象
        }
    }

正常情况进行AOP的地方(也就是第四步)

//正常情况进行AOP的情况
 public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
//earlyproxyReferences中存取的是哪些提前进行过AOP的bean,beanName:AOP之前的对象
//注意earlyproxyReferences中并没有存AOP的代理对象,BeanPostProcessor
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
//没有提前过Aop,则进行Aop
                return this.wrapIfNecessary(bean, beanName, cacheKey);
            }
        }

        return bean;
    }

 因此解决循环依赖,除了一二三级缓存,还有creatingSet和earlyProxyReferencs

简述就是:

a–>放进三级缓存–>注入b–>从单例池中找–>找不到就二级缓存–>再找不到就三级缓存中找,创建a后(可能是代理对象),将a从三级缓存中移除,放入二级中,将b放入单例池中,进行a中b的属性注入,判断若提前进行aop则,则跳过aop步骤,将a放入单例池,从二级缓存中把a拿掉。 

修改一下ABservice的循环依赖模式

 

 启动

报循环依赖的错误

此时加个@Lazy注解即可解决

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值