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和循环依赖处理 - 走看看
循环依赖这哥们写的更详细一些: