先说一下什么是循环依赖,Spring在初始化A的时候须要注入B,而初始化B的时候须要注入A,在Spring启动后这2个Bean都要被初始化完成java
Spring的循环依赖有两种场景面试
构造器的循环依赖,能够在构造函数中使用@Lazy注解延迟加载。在注入依赖时,先注入代理对象,当首次使用时再建立对象完成注入spring
属性的循环依赖主要是经过3个map来解决的缓存
@Component public class ConstructorA { private ConstructorB constructorB; @Autowired public ConstructorA(ConstructorB constructorB) { this.constructorB = constructorB; } }
@Component public class ConstructorB { private ConstructorA constructorA; @Autowired public ConstructorB(ConstructorA constructorA) { this.constructorA = constructorA; } }
@Configuration @ComponentScan("com.javashitang.dependency.constructor") public class ConstructorConfig { }
public class ConstructorMain { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConstructorConfig.class); System.out.println(context.getBean(ConstructorA.class)); System.out.println(context.getBean(ConstructorB.class)); } }
运行ConstructorMain的main方法的时候会在第一行就报异常,说明Spring没办法初始化全部的Bean,即上面这种形式的循环依赖Spring没法解决。bash
咱们能够在ConstructorA或者ConstructorB构造函数的参数上加上@Lazy注解就能够解决函数
@Autowired public ConstructorB(@Lazy ConstructorA constructorA) { this.constructorA = constructorA; }
由于咱们主要关注属性的循环依赖,构造器的循环依赖就不作过多分析了post
先演示一下什么是属性的循环依赖测试
@Component public class FieldA { @Autowired private FieldB fieldB; }
@Component public class FieldB { @Autowired private FieldA fieldA; }
@Configuration @ComponentScan("com.javashitang.dependency.field") public class FieldConfig { }
public class FieldMain { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(FieldConfig.class); // com.javashitang.dependency.field.FieldA@3aa9e816 System.out.println(context.getBean(FieldA.class)); // com.javashitang.dependency.field.FieldB@17d99928 System.out.println(context.getBean(FieldB.class)); } }
Spring容器正常启动,能获取到FieldA和FieldB这2个Beanthis
属性的循环依赖在面试中仍是常常被问到的。整体来讲也不复杂,可是涉及到Spring Bean的初始化过程,因此感受比较复杂,我写个demo演示一下整个过程代理
Spring的Bean的初始化过程其实比较复杂,为了方便理解Demo,我就把Spring Bean的初始化过程分为2部分
bean初始化过程完毕,则bean就能被正常建立出来了
下面开始写Demo,ObjectFactory接口用来生产Bean,和Spring中定义的接口同样
public interface ObjectFactory<T> { T getObject(); }
public class DependencyDemo { // 初始化完毕的Bean private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 正在初始化的Bean对应的工厂,此时对象已经被实例化 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 存放正在初始化的Bean,对象尚未被实例化以前就放进来了 private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16)); public <T> T getBean(Class<T> beanClass) throws Exception { // 类名为Bean的名字 String beanName = beanClass.getSimpleName(); // 已经初始化好了,或者正在初始化 Object initObj = getSingleton(beanName, true); if (initObj != null) { return (T) initObj; } // bean正在被初始化 singletonsCurrentlyInCreation.add(beanName); // 实例化bean Object object = beanClass.getDeclaredConstructor().newInstance(); singletonFactories.put(beanName, () -> { return object; }); // 开始初始化bean,即填充属性 Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); // 获取须要注入字段的class Class<?> fieldClass = field.getType(); field.set(object, getBean(fieldClass)); } // 初始化完毕 singletonObjects.put(beanName, object); singletonsCurrentlyInCreation.remove(beanName); return (T) object; } /** * allowEarlyReference参数的含义是Spring是否容许循环依赖,默认为true * 因此当allowEarlyReference设置为false的时候,当项目存在循环依赖,会启动失败 */ public Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); } } } } return singletonObject; } /** * 判断bean是否正在被初始化 */ public boolean isSingletonCurrentlyInCreation(String beanName) { return this.singletonsCurrentlyInCreation.contains(beanName); } }
测试一波
public static void main(String[] args) throws Exception { DependencyDemo dependencyDemo = new DependencyDemo(); // 伪装扫描出来的对象 Class[] classes = {A.class, B.class}; // 伪装项目初始化全部bean for (Class aClass : classes) { dependencyDemo.getBean(aClass); } // true System.out.println( dependencyDemo.getBean(B.class).getA() == dependencyDemo.getBean(A.class)); // true System.out.println( dependencyDemo.getBean(A.class).getB() == dependencyDemo.getBean(B.class)); }
是否是很简单?咱们只用了2个map就搞定了Spring的循环依赖
2个Map就能搞定循环依赖,那为何Spring要用3个Map呢?
缘由其实也很简单,当咱们从singletonFactories中根据BeanName获取相应的ObjectFactory,而后调用getObject()这个方法返回对应的Bean。在咱们的例子中
ObjectFactory的实现很简单哈,就是将实例化好的对象直接返回,可是在Spring中就没有这么简单了,执行过程比较复杂,为了不每次拿到ObjectFactory而后调用getObject(),咱们直接把ObjectFactory建立的对象缓存起来不就好了,这样就能提升效率了
好比A依赖B和C,B和C又依赖A,若是不作缓存那么初始化B和C都会调用A对应的ObjectFactory的getObject()方法。若是作缓存只须要B或者C调用一次便可。
知道了思路,咱们把上面的代码改一波,加个缓存。
public class DependencyDemo { // 初始化完毕的Bean private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // 正在初始化的Bean对应的工厂,此时对象已经被实例化 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 缓存Bean对应的工厂生产好的Bean private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 存放正在初始化的Bean,对象尚未被实例化以前就放进来了 private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16)); public <T> T getBean(Class<T> beanClass) throws Exception { // 类名为Bean的名字 String beanName = beanClass.getSimpleName(); // 已经初始化好了,或者正在初始化 Object initObj = getSingleton(beanName, true); if (initObj != null) { return (T) initObj; } // bean正在被初始化 singletonsCurrentlyInCreation.add(beanName); // 实例化bean Object object = beanClass.getDeclaredConstructor().newInstance(); singletonFactories.put(beanName, () -> { return object; }); // 开始初始化bean,即填充属性 Field[] fields = object.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); // 获取须要注入字段的class Class<?> fieldClass = field.getType(); field.set(object, getBean(fieldClass)); } singletonObjects.put(beanName, object); singletonsCurrentlyInCreation.remove(beanName); earlySingletonObjects.remove(beanName); return (T) object; } /** * allowEarlyReference参数的含义是Spring是否容许循环依赖,默认为true */ public Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; } }
咱们写的getSingleton的实现和org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)的实现如出一辙,这个方法几乎全部分析Spring循环依赖的文章都会提到,此次你明白工做原理是什么了把
[1]https://mp.weixin.qq.com/s/gBr3UfC1HRcw4U-ZMmtRaQ
[2]https://mp.weixin.qq.com/s/5mwkgJB7GyLdKDgzijyvXw
比较详细
[1]https://zhuanlan.zhihu.com/p/84267654
[2]http://www.noobyard.com/article/p-dbpjgfnq-ck.html