spring解决循环依赖思路

12 篇文章 0 订阅

代理

AProxy对象—>A代理对象—>A代理对象的target = A普通对象

A—>推断构造方法—>普通对象—>依赖注入—>初始化前—>初始化—>初始化后(AOP)—>代理对象—>放入单例池(三级缓冲的第一级缓冲)—>bean对象

@Component
public class UserService {
  	@Autowired
  	private OrderService orderService;
  	
  	public void test() {
			System.out.println(orderService);
    }
}

class AProxy extends A {
  
		A target;
		
		public void test() {
				//切面逻辑
      	//xxx
				target.test();// A普通对象.test()打印orderService,可以正常打印出来orderService
		}
}

public static void main() {
  	AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);	
  	//这里拿到的是userService的代理对象,cglb代理是继承实现的,所以代理对象可以强转为UserService
  	UserService userService = (UserService)ac.getBean(UserService.class);
  	userService.test();
}

注意:

1、普通对象是经过了依赖注入的,所以target.test()方法,执行正常

2、但是代理对象并没有进行依赖注入,没有必要

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LgcFPdl6-1679884802018)(../Pictures/typora-图片/循环依赖/image-20230326232024246.png)]

先执行”切面逻辑“,可以看到实际是代理对象的切面逻辑

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3UTyjw9K-1679884802019)(../Pictures/typora-图片/循环依赖/image-20230326232953616.png)]

普通对象的test()在打印orderService

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jT8uT8aF-1679884802019)(../Pictures/typora-图片/循环依赖/image-20230326233151506.png)]

bean生命周期简图

A—>无参构造方法—>普通对象—>依赖注入—>初始化前—>初始化—>初始化后(AOP)—>放入单例池(三级缓冲的第一级缓冲)—>bean对象

第三级:singletonFactories Map<String, ObjectFactory<?>>:目的就是打破循环的

第二级:earlySingletonObjects Map<String, Object>:保存提前需要给其他还没有经过完整生命周期bean(B、C)用的对象(可能是代理可能是普通)

第一级:singletonObjects Map<String, Object>:单例池,保存经过完整生命周期的bean

earlyProxyReferences:判断是否进行了AOP,避免第4步重复AOP

creatingSet:保存正在创建过程中的bean,判断是否出现循环依赖

第一步

A的bean生命周期:

  • 1、实例化—>A的普通对象

  • 2、填充B—>单例池—>创建B

    • B的生命周期
      • 2.1、实例化—>B普通对象
      • 2.2、填充A—>单例池—>创建A (循环依赖了)
      • 2.3、填充其他属性
      • 2.4、其他步骤(包括AOP)
      • 2.5、加入单例池
  • 3、填充其他属性

  • 4、其他步骤(包括AOP)

  • 5、加入单例池中

第二步

在创建B时,发现缺少A,发生了循环依赖,要解决就必须打破循环,要想打破循环,就必须在创建B时,A属性可以在一个地方被找到就打破了,所以要有一个地方要能找到A的对象,在创建B时能用。所以在创建A普通对象时,引入ZhouyuMap<beanName, Object>并放入,在创建B时,单例池找不到,就从ZhouyuMap找肯定就找到了,从而打破循环。

A的bean生命周期:

  • 1、实例化—>A的普通对象—>放入ZhouyuMap<beanName, Object>

  • 2、填充B—>单例池—>创建B

    • B的生命周期
      • 2.1、实例化—>B普通对象
      • 2.2、填充A—>单例池—>ZhouyuMap—>A普通对象
      • 2.3、填充其他属性
      • 2.4、其他步骤(包括AOP)
      • 2.5、加入单例池
  • 3、填充其他属性

  • 4、其他步骤(包括AOP)

  • 5、加入单例池中

(此时ZhouyuMap,到底算是第几级缓存,还不清楚)

第三步

上面虽然打破了循环,但是当A需要AOP时,就需要进行代理,产生代理对象,我们知道在spring中,经过AOP的bean,在单例池中是<beanName, 代理对象Proxy>形式存在的,而且代理对象的target属性指向A普通对象。此时上一步就存在问题,单例池中A是代理对象,但是实际赋值给B的确实A普通对象。

A的bean生命周期:

  • 1、实例化—>A的普通对象—>放入ZhouyuMap<beanName, Object>

  • 2、填充B—>单例池—>创建B

    • B的生命周期
      • 2.1、实例化—>B普通对象
      • 2.2、填充A—>单例池—>ZhouyuMap—>A普通对象
      • 2.3、填充其他属性
      • 2.4、其他步骤(包括AOP)
      • 2.5、加入单例池
  • 3、填充其他属性

  • 4、其他步骤(包括AOP)—>A代理对象

  • 5、加入单例池中

要想解决这个问题,也好办,那就是在2.2步从ZhouyuMap<beanName, Object>拿到是A的代理对象赋值给B即可,也就是说我们应该在第1步就创建A的代理对象,放入ZhouyuMap<beanName, Object>,且在第4步要能判断出来A是否进行过,至少AOP不能重复不能漏掉啊!

A的bean生命周期:

  • 1、实例化—>A的普通对象—>AOP—>A代理对象—>放入ZhouyuMap<beanName, Object>

  • 2、填充B—>单例池—>创建B

    • B的生命周期
      • 2.1、实例化—>B普通对象
      • 2.2、填充A—>单例池—>ZhouyuMap—>A代理对象
      • 2.3、填充其他属性
      • 2.4、其他步骤(包括AOP)
      • 2.5、加入单例池
  • 3、填充其他属性

  • 4、其他步骤(包括AOP)—>A代理对象

  • 5、加入单例池中

第四步

上步中“提前AOP“是在解决出现了循环依赖,还又要进行AOP想到的办法,但是不是每一个bean都要“提前进行AOP”,也就是说出现了循环依赖,才“提前进行AOP”

A的bean生命周期:

  • 1、实例化—>A的普通对象—>循环依赖—>AOP—>A代理对象—>放入ZhouyuMap<beanName, Object>

  • 2、填充B—>单例池—>创建B

    • B的生命周期
      • 2.1、实例化—>B普通对象
      • 2.2、填充A—>单例池—>ZhouyuMap—>A代理对象
      • 2.3、填充其他属性
      • 2.4、其他步骤(包括AOP)
      • 2.5、加入单例池
  • 3、填充其他属性

  • 4、其他步骤(包括AOP)—>A代理对象

  • 5、加入单例池中

现在问题就是,怎么判断出现了循环依赖?这里很明显是在2.2步发现了出现了循环依赖,这里就是”正在创建的bean,被别人依赖了“,A在“创建过程中”被B依赖了,就说明发生了循环依赖呗!所以我们要记录”正在创建的bean“,引入creatingSet记录正在创建的bean,在2.2步中,从单例池中没有获取到A,而且还在creatingSet中得知A正在创建,那肯定就发生了循环依赖。

A的bean生命周期:

  • 0、creatingSet[A]

  • 1、实例化—>A的普通对象

  • 2、填充B—>单例池—>创建B

    • B的生命周期
      • 2.1、实例化—>B普通对象
      • 2.2、填充A—>单例池—>creatingSet—>发生了循环依赖—>又要进行AOP则—>A代理对象
      • 2.3、填充其他属性
      • 2.4、其他步骤(包括AOP)
      • 2.5、加入单例池
  • 3、填充其他属性

  • 4、其他步骤(包括AOP)—>A代理对象

  • 5、加入单例池中

但是问题来了,假如此时A还有C依赖A呢?

第五步

A的bean生命周期:

  • 0、creatingSet[A]

  • 1、实例化—>A的普通对象

  • 2、填充B—>单例池—>创建B

    • B的生命周期
      • 2.1、实例化—>B普通对象
      • 2.2、填充A—>单例池—>creatingSet—>发生了循环依赖—>又要进行AOP则—>A代理对象
      • 2.3、填充其他属性
      • 2.4、其他步骤(包括AOP)
      • 2.5、加入单例池
    • C的生命周期
      • 3.1、实例化—>B普通对象
      • 3.2、填充A—>单例池—>creatingSet—>发生了循环依赖—>又要进行AOP则—>A代理对象
      • 3.3、填充其他属性
      • 3.4、其他步骤(包括AOP)
      • 3.5、加入单例池
  • 3、填充其他属性

  • 4、其他步骤(包括AOP)—>A代理对象

  • 5、加入单例池中

发现B、C分别创建了不同的A代理对象,这是有问题的。此时考虑是否可以将2.2中A的代理对象放入单例池中呢,创建C时从单例池中去除A的代理对象赋值给C,保证一个A代理对象,肯定是不能的,因为A的创建才走到第2步,而单例池中的对象是经过了完整的bean生命周期动作的对象,那又需要一个Map保证单例,那就新加一个Map呗,earlySingletonObjects(根据名称”提前单例对象“)。

A的bean生命周期:

  • 0、creatingSet[A]

  • 1、实例化—>A的普通对象

  • 2、填充B—>单例池—>创建B

    • B的生命周期
      • 2.1、实例化—>B普通对象
      • 2.2、填充A—>单例池—>creatingSet—>发生了循环依赖—>earlySingletonObjects(第一次肯定找不到)—>进行AOP—>A代理对象—>放入earlySingletonObjects—>赋值跟B
      • 2.3、填充其他属性
      • 2.4、其他步骤(包括AOP)
      • 2.5、加入单例池
    • C的生命周期
      • 3.1、实例化—>B普通对象
      • 3.2、填充A—>单例池—>creatingSet—>发生了循环依赖—>earlySingletonObjects(找到了)—>A代理对象—>赋值给C
      • 3.3、填充其他属性
      • 3.4、其他步骤(包括AOP)
      • 3.5、加入单例池
  • 3、填充其他属性

  • 4、其他步骤(包括AOP)—>A代理对象

  • 5、加入单例池中

第六步

我们发现B、C的A属性赋的值,都是从earlySingletonObjectsMap中拿的,earlySingletonObjectsMap中拿的对象是自己还没创建完,已经赋值给其他对象用了,所以叫”提前暴露“,earlySingletonObjectsMap就是为了”保存出现了循环依赖,提前给其他bean去用的代理bean“

但是还有个小问题,其实在2.2中A的AOP生成A的代理对象时,是需要A的普通对象的,否则代理是完不成的,因为代理对象的target指向普通对象,这么说循环还是没有解决,没有打破,所以还得有一个缓存去缓存A的普通对象去彻底打破循环,第三级缓冲singletonFactories,先简单理解为singletonFactoriesMap<String, A普通对象>,所以第三级缓冲作用就是打破循环。

A的bean生命周期:

  • 0、creatingSet[A]

  • 1、实例化—>A的普通对象—>singletonFactoriesMap<String, A普通对象>

  • 2、填充B—>单例池—>创建B

    • B的生命周期
      • 2.1、实例化—>B普通对象
      • 2.2、填充A—>单例池—>creatingSet—>发生了循环依赖—>earlySingletonObjects—>singletonFactoriesMap—>进行AOP—>A代理对象—>放入earlySingletonObjects—>赋值跟B
      • 2.3、填充其他属性
      • 2.4、其他步骤(包括AOP)
      • 2.5、加入单例池
    • C的生命周期
      • 3.1、实例化—>B普通对象
      • 3.2、填充A—>单例池—>creatingSet—>发生了循环依赖—>earlySingletonObjects(找到了)—>A代理对象—>赋值给C
      • 3.3、填充其他属性
      • 3.4、其他步骤(包括AOP)
      • 3.5、加入单例池
  • 3、填充其他属性

  • 4、其他步骤(包括AOP)—>A代理对象

  • 5、加入单例池中

但是实际第三级缓存存储的不是A的普通对象,而是一个ObjectFactory,这是一个函数式接口lamda表达式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p5QXI3T6-1679884802019)(../Pictures/typora-图片/循环依赖/image-20230326221137099.png)]

其实这里就能想通了,上面我们也说了,并不是每一个bean都需要AOP的,那么这个lamba表达式就是用来判断是否需要AOP的,在第1步放进去时,就只是singletonFactoriesMap.put进去一个表达式,并不会立即执行,在2.2步中从第三级缓冲中取出对应A的lamda表达式判断是否需要AOP。可以搜搜wraIfNecessary、getarlyBeanReference方法再详细看看。

第七步

A的bean生命周期:

  • 0、creatingSet[A]

  • 1、实例化—>A的普通对象—>singletonFactoriesMap<String, A的lamda>

  • 2、填充B—>单例池—>创建B

    • B的生命周期
      • 2.1、实例化—>B普通对象
      • 2.2、填充A—>单例池—>creatingSet—>发生了循环依赖—>earlySingletonObjects—>singletonFactoriesMap—>lamda
        • —>进行AOP—>A代理对象—>放入earlySingletonObjects—>赋值跟B
        • —>不进行AOP—>A普通对象—>放入earlySingletonObjects—>赋值跟B
      • 2.3、填充其他属性
      • 2.4、其他步骤(包括AOP)
      • 2.5、加入单例池
    • C的生命周期
      • 3.1、实例化—>B普通对象
      • 3.2、填充A—>单例池—>creatingSet—>发生了循环依赖—>earlySingletonObjects(找到了)—>A(这里就不用纠结找到的A是代理还是普通对象了)—>赋值给C
      • 3.3、填充其他属性
      • 3.4、其他步骤(包括AOP)
      • 3.5、加入单例池
  • 3、填充其他属性

  • 4、其他步骤(包括AOP,注意执行完lamda表达式后singletonFactories.remove了,就不会重复AOP了)

  • 5、earlySingletonObjects.get(A),加入单例池中

通过源码,发现singletonFactory.getObject()执行完lamda后,singletonObject对象可能是代理对象也可能是普通对象,会在singletonFactories.remove掉这个lamda表达式,也就是这个lamda表达式是一次性的,执行一次就够了,避免在第4步AOP重复。

AOP其实是一个插件,用户可以根据需求选择是否使用AOP,需要用时就在配置类中加上@EnableAspectJAutoProxy,然后在需要AOP增强的地方配置切点相关的即可使用了。所以第4步判断是否需要AOP,其实不是Spring做的事情,而是这个”插件AOP“去做的事情,

在这里插入图片描述

A的bean生命周期:

  • 0、creatingSet[A]

  • 1、实例化—>A的普通对象—>singletonFactoriesMap<String, A的lamda>

  • 2、填充B—>单例池—>创建B

    • B的生命周期
      • 2.0、creatingSet[A,B]
      • 2.1、实例化—>B普通对象—>singletonFactoriesMap<String, A的lamda>
      • 2.2、填充A—>单例池—>creatingSet发现了A—>则发生了循环依赖—>earlySingletonObjects—>singletonFactoriesMap—>lamda
        • —>进行AOP—>A代理对象—>放入earlySingletonObjects—>赋值给B
        • —>不进行AOP—>A普通对象—>放入earlySingletonObjects—>赋值给B
      • 2.3、填充其他属性
      • 2.4、其他步骤(包括AOP)
      • 2.5、加入单例池
    • C的生命周期
      • 3.1、实例化—>B普通对象
      • 3.2、填充A—>单例池—>creatingSet—>发生了循环依赖—>earlySingletonObjects(找到了)—>A(这里就不用纠结找到的A是代理还是普通对象了)—>赋值给C
      • 3.3、填充其他属性
      • 3.4、其他步骤(包括AOP)
      • 3.5、加入单例池
  • 3、填充其他属性

  • 4、其他步骤(包括AOP)—> earlyProxyReferences

  • 5、earlySingletonObjects.get(A),加入单例池中

@Lazy解决方式

问题

@Component
public class A {
  	private B b;
  	
  	public A(B b) {
      	this.b = b;
    }
}
@Component
public class B {
  	private A a;
  	
  	public B(A a) {
      	this.a = a;
    }
  
}

像上面这种情况,属性上没有@Autowired注解的,并且提供了有参构造(覆盖无参构造),此时发生循环依赖,A的普通对象都创建不出来的。

解决方式:

1、因为有参构造覆盖无参构造,所以另外提供一个无参构造方法即可

2、在这个有参构造方法上面加上@Lazy注解,大致解决思路就是:给B生成一个代理对象(注意这个代理和AOP代理没有任何关系),这个代理对象也是CGLB继承式的继承了B,然后把这个代理对象B赋值给A的B属性,从而A对象创建完成,放入单例池,然后创建B,从单例池取出A赋值给B的A属性。直接没有发生循环依赖;你可能会说此时beanB和B的代理对象不是一个对象啊?是的,不是一个对象,但是实际上,不影响真实调用的,例如执行beanA的方法用到了b属性的方法,此时b代理对象中执行时是先从容器中找到beanB对象,再去执行beanB的方法。

class BProxy extends B {
		//这个代理和AOP代理不同,没有target
		public void test() {
				//从容器中找beanB
      	//执行beanB的test()
				beanB.test();
		}
}

总结

在这里插入图片描述

第三级:singletonFactories Map<String, ObjectFactory<?>>:目的就是打破循环的

第二级:earlySingletonObjects Map<String, Object>:保存提前需要给其他还没有经过完整生命周期bean(B、C)用的对象(可能是代理可能是普通)

第一级:singletonObjects Map<String, Object>:单例池,保存经过完整生命周期的bean

earlyProxyReferences:判断是否进行了AOP,避免第4步重复AOP

creatingSet:保存正在创建过程中的bean,判断是否出现循环依赖

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值