关于java:重说循环依赖

49次阅读

共计 4636 个字符,预计需要花费 12 分钟才能阅读完成。

重说循环依赖

本文次要介绍 循环依赖相干问题

author: HuiFer

github_repo: https://github.com/huifer/spr…

gitee_repo: https://gitee.com/pychfarm_ad…

Java 循环依赖解决

在开始正式剖析之前咱们须要思考一个问题. 在 java 中如何创立出一个循环援用的对象, 上面这段代码就是一个创立循环援用的形式.

public class Demo {public static void main(String[] args) {A a = new A(); B b = new B(); a.innerB = b; b.innerA = a; System.out.println(); }}
class A {public B innerB;}
class B {public A innerA;}

上述代码的语义很简略: 创立出对象而后将援用关系绑定.

  • 通过下面代码能够得出上面的一个流程

    1. 创建对象
    2. 设置属性
    3. 设置曾经初始化过的属性对象
    4. 设置未初始化过的属性对象

    依据这个流程咱们须要编写出一个 java 代码来解决这个问题

首先第一步须要解决的是一个保留曾经初始化的对象. 这里为了不便将 className 作为 key, 实例作为 value

 /** * key: className * value: object instance */ static Map<String, Object> nameMappingObjectInstance = new HashMap<>();

容器筹备好了之后就是对单个类的解决.

  1. 创建对象
  2. 设置属性

首先搭建出一个整体框架, 次要须要解决的是属性设置setProperty

 public static <T> T getBean(Class<T> clazz) throws Exception {String className = clazz.getName(); // 容器中存在间接获取
 if (nameMappingObjectInstance.containsKey(className)) {return (T) nameMappingObjectInstance.get(className); } // 容器中不存在. 手动创建对象
 // 通过无参结构创立
 T objectInstance = clazz.getDeclaredConstructor().newInstance(); // 存入容器
 nameMappingObjectInstance.put(className, objectInstance); // 设置创建对象的数据
 setProperty(objectInstance); return objectInstance; }

属性设置的代码如下

private static <T> void setProperty(T objectInstance) throws Exception {Field[] fields = objectInstance.getClass().getDeclaredFields(); for (Field field : fields) {field.setAccessible(true); // 获取属性类型
 Class<?> fieldType = field.getType(); String fieldClassName = fieldType.getName(); // 从容器中获取
 Object cache = nameMappingObjectInstance.get(fieldClassName); if (cache != null) {field.set(objectInstance, cache); } else { // 手动创立
 Object bean = getBean(fieldType); field.set(objectInstance, bean); }
 } }

这段代码次要是将字段进行循环, 而后设置数据. 在设置数据的时候尝试从容器中获取, 如果没有则调用 getBean 进行获取.
程序执行流程图


具体再看流程 6

通过上述办法将 B 信息设置后跳出回到流程 6. 将 A 的属性 B 设置.
设置后实现循环依赖解决
顺次就实现了循环依赖的处理过程. 残缺代码如下

public class Demo {/** * key: className * value: object instance */ static Map<String, Object> nameMappingObjectInstance = new HashMap<>();
 public static void main(String[] args) throws Exception {A a = new A(); B b = new B(); a.innerB = b; b.innerA = a;
 A bean = getBean(A.class); System.out.println();}
 public static <T> T getBean(Class<T> clazz) throws Exception {String className = clazz.getName(); // 容器中存在间接获取
 if (nameMappingObjectInstance.containsKey(className)) {return (T) nameMappingObjectInstance.get(className); } // 容器中不存在. 手动创建对象
 // 通过无参结构创立
 T objectInstance = clazz.getDeclaredConstructor().newInstance(); // 存入容器
 nameMappingObjectInstance.put(className, objectInstance); // 设置创建对象的数据
 setProperty(objectInstance); return objectInstance; }
 private static <T> void setProperty(T objectInstance) throws Exception {Field[] fields = objectInstance.getClass().getDeclaredFields(); for (Field field : fields) {field.setAccessible(true); // 获取属性类型
 Class<?> fieldType = field.getType(); String fieldClassName = fieldType.getName(); // 从容器中获取
 Object cache = nameMappingObjectInstance.get(fieldClassName); if (cache != null) {field.set(objectInstance, cache); } else { // 手动创立
 Object bean = getBean(fieldType); field.set(objectInstance, bean); }
 } }}

在理解 Java 循环依赖的解决形式后再来看 Spring 中的解决形式

Spring 循环依赖解决

  • Spring 中解决循环依赖的形式和咱们下面的思路根本相似
  • 在开始剖析循环依赖之前咱们须要一段代码来模仿一个循环依赖
@Component
public class LoopBean {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(LoopBean.class); B bean = ctx.getBean(B.class); System.out.println();}
 @Component(value = "a") public class A {@Autowired @Qualifier("b") private B b;
 public B getB() { return b;}
 public void setB(B b) {this.b = b;} }
 @Component(value = "b") public class B {@Autowired @Qualifier("a") private A a;
 public A getA() { return a;}
 public void setA(A a) {this.a = a;} }}

Spring 中通过 doCreateBean 创立 bean 实例.
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
在办法内这样一些代码.

上图中两个红框内
​ 第一段作用是将 ObjectFactory 放入容器
​ 第二段作用时设置属性值
doCreateBean 代码中定义了 BeanWrapper 这个能够了解成 bean 的包装, 比照笔者所写的那段代码能够简略了解成 BeanWrapper 中存储了一个 Object 实例

这个状态就是创立 bean 实例状态. 持续往下走会有设置属性的代码

这里进行属性设置, 在属性设置的时候会调用 getSingleton , 在这里解决了循环依赖。在看代码之前先看几个变量

  1. singletonFactories

  1. singletonObjects


从下面两个截图能够晓得单例对象的容器中还没有 a b 两个实例. 他们还在 ObjectFactory 容器中期待被应用
退出 ObjectFactory 容器的办法: addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
到目前为止数据曾经筹备实现, 接下来就是获取对象的代码了 getSingleton
咱们当初正在设置 "a"的属性 b

此时当初信息筹备的都是 b 的
会在这里将信息设置到 singletonObjectearlySingletonObjects
这里也正好说一下 earlySingletonObjects 是什么意思.
正如咱们当初所处的状况: 设置 A 的属性 B, B 还没有被加载过、初始化过, 此时 B 还没有, 然而通过后面的一些操作 Spring 将 B 对象在里头创立好了放在容器中期待咱们应用

通过这段代码将 B 这个实例放在一个容器中. 当初咱们须要对 A 的 B 属性进行赋值, 从中获取.

earlySingletonObjects

/**
 *  Cache of early singleton objects: bean name to bean instance. * * 提前裸露的对象 key: beanName , value: object * */private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

earlySingletonObjects:提前裸露的 bean. 在属性设置之前通过 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference 创立
持续回到主线代码
当初获取到了 B 对象, 然而属性还没有设置, 此时还会持续调用 getSingletondoCreateBean
当 B 信息设置实现后

此时就和咱们那个代码很像了
当初 A 在单例对象容器外面了. B 还在 提前裸露的容器中.
最初当咱们通过 getBean 获取 B 实例的是时候.
将信息再通过上文所述办法设置属性 …. 最终失去上面这样的对象

正文完
 0