1.什么是循环依赖?
所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依 赖 A。它们之间的依赖关系如下:
2.一级缓存和二级缓存原理分析
2.1 一级缓存的作用
我们可以简单手写实现下创建bean的过程,如下:
重点关注getBean方法,过程已在注释中标明。
@Component
public class InstanceA {
@Autowired
private InstanceB instanceB;
}
@Component
public class InstanceB {
@Autowired
private InstanceA instanceA;
}
public class MainStart {
//用于存放bean定义,key为beanName,value为beanDefinition
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
//一级缓存
private static Map<String,Object> singletonObjects = new ConcurrentHashMap<>();
/**
* 读取bean定义,并注册到beanDefinitionMap中,当然在spring中肯定是根据配置动态扫描注册
*/
public static void loadBeanDefinitions() {
RootBeanDefinition aBeanDefinition=new RootBeanDefinition(InstanceA.class);
RootBeanDefinition bBeanDefinition=new RootBeanDefinition(InstanceB.class);
beanDefinitionMap.put("instanceA",aBeanDefinition);
beanDefinitionMap.put("instanceB",bBeanDefinition);
}
public static void main(String[] args) throws Exception {
//1. 加载所有的BeanDefinition
loadBeanDefinitions();
// 2.循环bean定义,根据beanName创建Bean
for (String beanName : beanDefinitionMap.keySet()){
// 2.1 此时,先创建instanceA
getBean(beanName);
}
}
/**
* 根据bean名称创建bean
* @param beanName bean的名称
*/
private static Object getBean(String beanName) throws Exception {
//==================实例化=========================
//1.从bean定义容器中获取bean定义对象
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
//2.从bean定义中获取到当前创建的bean的class对象
Class<?> beanClass = beanDefinition.getBeanClass();
//3.利用反射实例化bean
Object instanceBean = beanClass.newInstance();
//==================属性赋值=========================
//4.获取到该bean所有的属性,判断有无@Autowired注解
Field[] declaredFields = instanceBean.getClass().getDeclaredFields();
for (Field field : declaredFields) {
Autowired annotation = field.getAnnotation(Autowired.class);
if(annotation != null){
//由于属性是private,设置修改权限
field.setAccessible(true);
//4.1 表明该属性上有@Autowired属性,比如:
// @Autowired
// private InstanceB instanceB;
//这时,再根据名称instanceB递归调用此方法
Object fieldObject = getBean(field.getName()); //得到instanceB对象
// 4.2 将创建好的instanceB bean赋值给此属性
field.set(instanceBean,fieldObject);
}
}
//==================初始化===========================
//省略。。
//===========将创建的bean的放入一级缓存中===============
//5.放入一级缓存
singletonObjects.put(beanName,instanceBean);
return instanceBean;
}
}
我们运行上面的main方法,会发现出现栈内存溢出。主要是出现了死循环导致的。
我们分析下这个过程:
当我们在创建InstanceA时,会先去实例化,然后进行属性赋值,在属性赋值的时候,发现要注入InstanceB,这时就会去创建InstanceB,即递归getBean方法;在创建InstanceB的过程中,又要对InstanceB的属性进行赋值,这时又发现要注入属性InstanceA,然后又去调用getBean去创建B,陷入死循环中。过程如下图(左侧):
如上图(右侧),在创建InstanceB的过程中,当进行属性赋值的时候发现有一个属性InstanceA,这时需要提供一个出口,而不是直接再去创建InstanceA。我们可以提供一个getSingleton方法,先去一级缓存中获取,如果发现缓存中有该bean,则进行返回该bean,而不需要再次去实例化、属性赋值、初始化…。除此之外,我们需要在属性赋值之前,即实例化之后,就得将bean放入一级缓存中,不然在getSingleton中获取的时候获取不到,此时一级缓存中存放的便是只完成了实例化的不完整Bean。
代码修改如下:
private static Object getBean(String beanName) throws Exception {
//>>>>>>>>>>>添加出口代码<<<<<<<<<<<<<<
Object singleton = getSingleton(beanName);
if(singleton != null){
return singleton;
}
//==================实例化=========================
//1.从bean定义容器中获取bean定义对象
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
//2.从bean定义中获取到当前创建的bean的class对象
Class<?> beanClass = beanDefinition.getBeanClass();
//3.利用反射实例化bean
Object instanceBean = beanClass.newInstance();
//>>>>>>>>>>>实例化后,将bean放入一级缓存<<<<<<<<<<<<<<
singletonObjects.put(beanName,instanceBean);
//==================属性赋值=========================
//4.获取到该bean所有的属性,判断有无@Autowired注解
Field[] declaredFields = instanceBean.getClass().getDeclaredFields();
for (Field field : declaredFields) {
Autowired annotation = field.getAnnotation(Autowired.class);
if(annotation != null){
field.setAccessible(true);
//4.1 表明该属性上有@Autowired属性,比如:
// @Autowired
// private InstanceB instanceB;
//这时,再根据名称instanceB递归调用此方法
Object fieldObject = getBean(field.getName()); //得到instanceB对象
// 4.2 将创建好的instanceB bean赋值给此属性
field.set(instanceBean,fieldObject);
}
}
//==================初始化===========================
//省略。。
//===========将创建的bean的放入一级缓存中===============
//5.放入一级缓存
// singletonObjects.put(beanName,instanceBean);
return instanceBean;
}
private static Object getSingleton(String beanName){
//1.如果一级缓存中有,则返回一级缓存中的bean
if(singletonObjects.containsKey(beanName)){
return singletonObjects.get(beanName);
}
return null;
}
运行结果:
2.2 二级缓存的作用
在上面代码中,一级缓存中存放的是不完整的bean。在多线程环境下,如果在创建InstanceA的时候,另外一个线程去getBean(InstanceA),那么将会获取到一级缓存中不完整的bean,即没有完成属性赋值以及初始化的bean。所以,我们需要引入二级缓存。一级缓存还是像我们刚开始那样,存放完整的bean,二级缓存中存放不完整的bean。
流程图如下:
代码修改如下:
//二级缓存,用于存放不完整的bean,将完整bean和不完整bean分离开来
private static Map<String,Object> earlySingletonObjects = new ConcurrentHashMap<>();
private static Object getBean(String beanName) throws Exception {
//>>>>>>>>>>>添加出口代码<<<<<<<<<<<<<<
Object singleton = getSingleton(beanName);
if(singleton != null){
return singleton;
}
//==================实例化=========================
//1.从bean定义容器中获取bean定义对象
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
//2.从bean定义中获取到当前创建的bean的class对象
Class<?> beanClass = beanDefinition.getBeanClass();
//3.利用反射实例化bean
Object instanceBean = beanClass.newInstance();
//修改:>>>>>>>>>>>实例化后,将bean放入二级缓存<<<<<<<<<<<<<<
//将bean放入二级缓存中
earlySingletonObjects.put(beanName,instanceBean);
//==================属性赋值=========================
//4.获取到该bean所有的属性,判断有无@Autowired注解
Field[] declaredFields = instanceBean.getClass().getDeclaredFields();
for (Field field : declaredFields) {
Autowired annotation = field.getAnnotation(Autowired.class);
if(annotation != null){
field.setAccessible(true);
//4.1 表明该属性上有@Autowired属性,比如:
// @Autowired
// private InstanceB instanceB;
//这时,再根据名称instanceB递归调用此方法
Object fieldObject = getBean(field.getName()); //得到instanceB对象
// 4.2 将创建好的instanceB bean赋值给此属性
field.set(instanceBean,fieldObject);
}
}
//==================初始化===========================
//省略。。
//===========将创建的bean的放入一级缓存中===============
//5.放入一级缓存
singletonObjects.put(beanName,instanceBean);
return instanceBean;
}
private static Object getSingleton(String beanName){
//1.如果一级缓存中有,则返回一级缓存中的bean
if(singletonObjects.containsKey(beanName)){
return singletonObjects.get(beanName);
}else if(earlySingletonObjects.containsKey(beanName)){
//2.一级缓存中没有,则去二级缓存中获取
return earlySingletonObjects.get(beanName);
}
return null;
}
3.三级缓存原理分析
前面在二级缓存的时候,就已经解决循环依赖问题了,为什么还要引入三级缓存?1.解决循环依赖过程中的AOP增强bean情况,2.解耦,单一职责原则。
3.1 二级缓存可不可以解决AOP?
我们写一个后置处理器来简单实现下:
public class JdkProxyBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
// 假设A被切点命中,需要创建代理 @PointCut("execution(* *..InstanceA.*(..))")
if (bean instanceof InstanceA) {
JdkDynimcProxy jdkDynimcProxy = new JdkDynimcProxy();
return jdkDynimcProxy.getProxy(bean.getClass());
}
return bean;
}
}
在bean实例化之后,无论存不存在循环依赖,我们都在此处判断是否需要AOP增强,如果需要则返回代理后的Bean,不需要,返回之前的bean。然后,将此bean放入二级缓存中。当InstanceB需要注入InstanceA时,去二级缓存中获取即可,获取的也是AOP增强的bean。二级缓存是可以解决AOP的。
3.2 为什么还需要三级缓存?
在实例化之后就创建bean,这样是违背Spring的设计原则的。Spring结合AOP和Bean的生命周期,是在Bean创建完成之后,通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成,在这个后置处理器的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。所以,要满足两点:
1.能判断是否存在循环依赖,若存在,才会在初始化之后就创建动态代理bean
2.AOP增强需要在Bean创建完成后再去操作,如何在初始化之后,去增强实例化之后的bean呢?函数式接口进行回调。
3.2.1 判断是否存在循环依赖
添加一个Set集合,用于存放正在创建的Bean的name,我们在bean创建之前,将BeanName放入Set中,表明此bean正在创建。如果一级缓存中没有,但是Set里面有,则说明该Bean创建过程中存在循环依赖。
//循环依赖标识
private static Set<String> singletonsCurrentlyInCreation = new HashSet<>();
private static Object getSingleton(String beanName){
//1.如果一级缓存中有,则返回一级缓存中的bean
Object bean = singletonObjects.get(beanName);
//2.如果一级缓存中没有,并且正在创建列表中有,则说明存在循环依赖
if(bean == null && singletonsCurrentlyInCreation.contains(beanName)){
//解决循环依赖。。。。。
}
return bean;
}
3.2.2 三级缓存实现接口回调
//三级缓存,value存放的不是bean对象,而是函数式接口
private static Map<String,ObjectFactory> singletonFactories = new ConcurrentHashMap<>();
注意:三级缓存存放的是函数式接口,而不是Bean对象。引入三级缓存后,我们可以在实例化之后,放入三级缓存中。
//3.利用反射实例化bean
Object instanceBean = beanClass.newInstance();
//修改:…………………………………………存入三级缓存………………………………………………………………
singletonFactories.put(beanName, () -> new JdkProxyBeanPostProcessor().getEarlyBeanReference(instanceBean, beanName));
//此时,不需要存入二级缓存,因为这时的bean需不要代理,无法确定,二级缓存中也不确定存什么对象
// earlySingletonObjects.put(beanName,instanceBean);
//==================属性赋值=========================
//4.获取到该bean所有的属性,判断有无@Autowired注解
在getSingleton方法修改如下:
我们可以发现在此方法中,两个return返回的结果分别是二级缓存中的bean以及一级缓存中的bean。而在这个过程中,三级缓存做的就是调用回调方法进行判断是否要生成动态代理对象,以及将处理后的bean放入二级缓存中。
private static Object getSingleton(String beanName){
//1.如果一级缓存中有,则返回一级缓存中的bean
Object bean = singletonObjects.get(beanName);
//2.如果一级缓存中没有,并且正在创建列表中有,则说明存在循环依赖
if(bean == null && singletonsCurrentlyInCreation.contains(beanName)){
//对于下面过程5的补充
//从二级缓存中获取
if(earlySingletonObjects.containsKey(beanName)){
return earlySingletonObjects.get(beanName);
}
//3.从三级缓存中获取
ObjectFactory factory = singletonFactories.get(beanName);
if(factory != null){
//4.调用getObject方法,进行回调,处理AOP
Object object = factory.getObject();
//5.放入二级缓存中,避免InstanceA被依赖多次,创建多次动态代理对象的情况,所以,在三级缓存之前应加入二级缓存判断
earlySingletonObjects.put(beanName,object);
//6.移除三级缓存中的信息
singletonFactories.remove(beanName);
}
}
return bean;
}
在放入一级缓存前,加入对二级缓存的判断,如果有的话要从二级缓存里面获取。
//==================初始化===========================
//省略。。
//添加二级缓存的判断,从二级缓存中获取,因为有可能是动态代理的bean,而不是最初的原生bean
if(earlySingletonObjects.containsKey(beanName)){
instanceBean = earlySingletonObjects.get(beanName);
}
//===========将创建的bean的放入一级缓存中===============