spring之循环依赖底层源码分析

目录

前言:

@Resouce / @AutoWired区别

@Resouce注入过程

spring循环依赖底层原理源码解析

解决循环依赖问题所需要的重要关键对象

循环依赖不同场景解决方式


前言:

       今天聊一聊spring循环依赖问题,同时分析一下@Resouce / @AutoWired区别。


@Resouce / @AutoWired区别

      @Autowired注解是Spring提供的,而@Resource注解是J2EE本身提供的

      @Autowird注解默认通过byType方式注入,而@Resource注解默认通过byName方式注入

      @Autowired注解注入的对象需要在IOC容器中存在,否则需要加上属性required=false,表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错。

     @Autowired是通过 AutowiredAnnotationBeanPostProcessor 来实现的

     @Resouce 是通过 CommonAnnotationBeanPostProcessor 来实现的


@Resouce注入过程

1. 寻找标记了@Resouce注入点

       包括从字段或方法上查找,符合条件的会生成ResourceElement对象,最后将找到的所有标记了该注解的注入点放到LinkedList集合中。

2. @Resouce属性值注入,遍历每个注入点进行注入

        2.1 如果使用@Resouce时没有指定具体的name,那么则用field的name,或setxxx()中的xxx。拿属性的名字去BeanFactory中有没有该bean,如果有直接返回。如果@Resouce指定了name那么就按照name去getBean,如果找不到就是找不到。

        2.2 如果没有指定name并且拿属性的名字去BeanFactory中有没有该bean,则继续判断属性上标记了@Lazy注解,如果会直接返回一个代理对象注入给属性。如果没有得话会和@Autowired注解得工作原理一样,先byType再byName。


spring循环依赖底层原理源码解析

假设场景:

        AService的Bean对象内部包含BService对象

        BService的Bean对象内部包含AService对象

   AService出现了循环依赖的情况下,需要提前进行AOP,如何才能知道是否出现了循环依赖呢?

       在Bean一开始创建的时候给一个属性用来记录当前bean是否还在创建中。通过判断是否还在创建中确定是否出现循环依赖问题。

先创建AService,执行AService的生命周期
0. singletonCurrentlyInCreation:在一开始创建的时候记录bean的创建状态
1. 实例化AService-->得到一个对象--SMap<AService:  Lambda(AService原始对象)>
    根据原始对象生成一个Lambda表达式
2. 填充BService属性 -- > 去单例池找BService-- >再去SMap去找--> 没有则创建BService
3. 填充其他属性/填充CService属性
4. 初始化前、初始化
5. 初始化后
6. 放入单例池


创建BService,执行BService的生命周期
2.1. 实例化BService-->得到一个对象-->存入SMap<BService: BService原始对象>
2.2. 填充AService属性
-- > 去单例池找AService(一级缓存 )
-- >singletonCurrentlyInCreation(判断是否正在创建)
-- >正在创建得话就出现了循环依赖
-- >earlySingletonObjects(二级缓存中去找是否有 AOP代理对象)
-- >如果没有就通过三级缓存的Lambda表达式提前进行AOP
-- >得到AService代理对象
-- >放入二级缓存中
如果不需要进行AOP就将三级缓存中原始对象放入二级缓存,并且会移除对应三级缓存内容
2.3. 填充其他属性
2.4. 初始化前、初始化
2.5. 初始化后
2.6. 放入单例池


创建CService,执行CService的生命周期
3.1. 实例化CService-->得到一个对象-->存入SMap<CService: CService原始对象>
3.2. 填充AService属性 
-- > 去单例池找AService
-- > 没找到则判断singletonCurrentlyInCreation是否正在创建
-- >如果正在创建则表示出现循环依赖问题
-- >去二级缓存中去找
-- >因为创建了BService所以在二级缓存中可以找到,就返回AService代理对象。
3.3. 填充其他属性
3.4. 初始化前、初始化
3.5. 初始化后
3.6. 放入单例池

注意:添加CService是为了解决可能出现的问题,如果判断出当前需要进行AOP就去创建代理对象的话,那么BService会创建一个代理对象,CService也会创建一个代理对象。保证生成对象单例。

解决循环依赖问题所需要的重要关键对象

三级缓存:

      1.singletonObjects:单例池(一级缓存),存放经过完整生命周期的bean

      2.earlySingletonObjects: 二级缓存,存放的是未经过完整生命周期的bean对象,比如原始对象或提前进行AOP的对象。 存放的就是三级缓存执行后的结果。

       3.singletonFactories:SMap 表达式中存放的是一段逻辑,这个逻辑会判断到底返回原始对象还是代理对象。

        Lambda表达式中有beanName、beandefinition、bean(原始对象)

额外:

       1. singletonCurrentlyInCreation:在一开始创建的时候记录bean的创建状态

       2. earlyProxyReferences:用来记录某个原始对象是否进行过AOP 了。

循环依赖不同场景解决方式

1. 原型bean情况下的循环依赖解析

        不能解决

2. 构造方法导致的循环依赖解析

        @Lazy

3. @Async情况下的循环依赖解析

       使用@Async会异步生成一个代理对象,使用@Lazy注解直接生成代理对象返回,在异步使用的时候会根据name去找代理对象, 此时找到的bean与@Lazy注解生成的bean是同一个。

4. @Transaction情况下的循环依赖解析

不会报错,因为@Async是新添加了一个BeanPostProcessor 而 @Transaction添加的是advisor

5. BeanFactoryAware下的循环依赖解析

        自己注入自己不会报错

  • 39
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring循环依赖是指两个或多个Bean之间相互依赖,形成一个循环引用的关系。在Spring容器启动的过程中,如果存在循环依赖,那么默认情况下会抛出BeanCurrentlyInCreationException异常。 下面是Spring循环依赖源码分析: 1. 当我们向Spring容器中注入一个Bean时,Spring会先检查这个Bean是否已经正在创建中(正在创建的Bean是无法注入的),如果正在创建中,则直接返回一个早期引用,否则继续创建Bean。 2. 在Bean的创建过程中,当遇到依赖注入(如@Autowired注解)时,Spring会检查要注入的Bean是否已经在创建中。如果是,则返回一个代理对象作为占位符,等待真正的Bean创建完毕后再进行注入。 3. Spring使用三级缓存来解决循环依赖问题。第一级缓存是单例池,存放已经创建好的单例Bean。第二级缓存是提前暴露的ObjectFactory,存放早期引用。第三级缓存是用于存放正在创建中的Bean的缓存,用于检测循环依赖。 4. 当检测到循环依赖时,Spring会尝试使用构造函数的方式完成循环依赖。它会先创建一个空对象,并将其放入到第三级缓存中。然后调用构造函数去创建这个Bean,此时依赖的Bean会返回一个早期引用。最后,将这个Bean加入到第一级缓存中,并开始注入依赖。 5. 当所有的Bean都创建完成后,Spring会触发后置处理器的回调方法,完成Bean的初始化。 总结:Spring循环依赖通过使用三级缓存和构造函数来解决,在Bean创建过程中动态地判断和处理循环依赖关系,确保所有的Bean都能被正确地创建和注入。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值