spring bean的创建源码及循环依赖的解决办法

bean的创建过程:
        类的构造方法创建普通对象-->依赖注入(IOC)-->初始化前-->初始化-->初始化后(通过AOP生成代理对象)-->代理对象-->放入单例Map(beanFactory)-->bean对象
 

循环依赖的现象:

1、存在两个bean对象A和B,A中引入了B、B中引入A;

2、当创建A对象时依赖注入B对象,B对象不存在就会开始B对象的创建流程,然后注入A对象的bean,此时A对象没有完成bean对象的创建流程,A对象的bean也是不存在的,因此A和B都无法创建进入死循环;

spring使用三级缓存来解决循环依赖问题:

1、在创建对象之前将对象的名称(beanName)放在singletonsCurrentlyInCreation(Set<String>)里面、实例A放入第三级缓存;

2、在创建B对象时在singletonObjects(一级) 中找A对象,不存在则B会在earlySingletonObjects(二级)中查找A对象,也不存在;

3、然后在singletonsCurrentlyInCreation找beanName、存在A,此时会从第三级缓存中获得A对象的普通对象来进行AOP并将AOP后的代理对象放入到二级缓存中(删除三级缓存);

4、然后B对象注入A对象完成bean的创建,继续开始A对象的创建流程:注入B对象完成创建,完成创建后放入singletonObjects中(删除二级缓存)

tips:我们平时所说的beanFactory就是第一级缓存singletonObjects ;

 二级缓存的作用是对以下情况:

       有A、B、C三个类,A中有B和C,B和C中都有A,创建B对象时会将A对象的bean放在二级缓存中,创建C对象时直接从二级缓存中取A对象不再重新创建;

       如果没有二级缓存,则B会创建A对象的bean、C会创建A对象的bean,不符合spring的单例原则,所以二级缓存放的是提前进行AOP的bean对象、是没有进行依赖注入的bean;

一级缓存:Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

二级缓存:Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

三级缓存:Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

ConcurrentHashMap的说明:

面试被问到 ConcurrentHashMap答不出 ,看这一篇就够了!_Java烂猪皮V的博客-CSDN博客

Bean对象的创建分两部分:

    1、dao层的bean创建,比controller和service的创建要早一些:因为dao层一般都是接口,使用mapperScan进行扫描package,在注解mapperScan中的MapperScannerRegister扫描package中的dao文件进行bean定义(processBeanDefinitions),将beanClass设置为MapperFactoryBean(实现FactoryBean),MapperFactoryBean实现了FactoryBean的getObject方法,getObject使用jdk的动态代理来生成bean放在beanfactory中

  

    2、controller和service层的bean创建:

     我们使用springboot项目,在启动类的main方法中进入 SpringApplication.run-->……SpringApplication的run方法中的refreshContext-->AbstractApplicationContext.refresh()--> finishBeanFactoryInitialization(beanFactory) 实例化非懒加载的bean  -->  preInstantiateSingletons()-->getBean()-->doGetBean()-->getSingleton(createBean())

其中createBean()-->doCreateBean()用于创建代理对象并放入到缓存中

               1、initializeBean-->applyBeanPostProcessorsAfterInitialization-->postProcessAfterInitialization(AbstractAutoProxyCreator重写)-->wrapIfNecessary()-->createProxy(...) 最后面选择jdk或cglib创建代理对象

               2、addSingletonFactory()  存储三级缓存,三级缓存中存放的是ObjectFactory,ObjectFactory的getObject方法获得bean

        getSingleton()     存储二级缓存,移除三级缓存

1、如果是单例则调用getSingleton(中的createBean()创建对象),getSingleton中的beforeSingletonCreation方法将beanName放在 singletonsCurrentlyInCreation(Set<String>)中,2、bean创建完后调用 afterSingletonCreation()移除singletonsCurrentlyInCreation中的beanName 

3、addSingleton()将bean放入singletonObjects(一级缓存)单例池、registeredSingletons,移除earlySingletonObjects(二级缓存)、singletonFactories中的bean

dao层的bean都创建完之后,现在来看controller和service层的bean定义阶段,用断点来看,dao层的接口都跳转到了(isFactoryBean(beanName))这段逻辑里面,直接从一级缓存中获取的bean,controller和service走的是最下面的getBean(beanName);

        在选择代理类型的地方打个断点,发现controller和service的bean创建都是用的Cglib动态代理,常见的说法是实现了接口的类会使用Jdk的动态代理,实际上service层也是使用的cglib动态代理;

        因为我们实例化bean时根据注解读取,而 @Service 这个注解都是在实现类上,所有targetClass.isInterface()都是false;

        因为接口不能实例化、即不能调用a.getCalss().isInterface()方法,所以目前并有找到isInterface()为true的场景;

spring项目的启动流程代码:

      AnnotationConfigApplicationContext构造方法的refresh()--> 

      后面的步骤就和上面springboot一样了

以上解决了生成原始对象后IOC注入循环依赖的问题,这种方式解决不了构造方法的循环依赖

如 A类 构造方法  public AService(BService bService){}

    B类构造方法    public BService(AService aService){}

此时在创建原始对象时就出现了循环依赖,解决方法是在AService的构造方法上添加注释@Lazy

@Lazy的原理是先创建一个代理对象注入解决循环依赖,等到用的是有再创建实例对象;

解决这种循环依赖还有其他方式,见以下博客:

SpringBoot(6)— Bean懒加载@Lazy和循环依赖处理 - 走看看

循环依赖这哥们写的更详细一些:

Spring发生循环依赖的原因以及Spring是如何解决循环依赖的_北境之旅的博客-CSDN博客_循环依赖产生的原因

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值