美文网首页技术干货
Spring中的循环依赖

Spring中的循环依赖

作者: BrightLoong | 来源:发表于2018-09-16 17:50 被阅读57次
circle 阅读原文请访问我的博客BrightLoong's Blog

什么是循环依赖

循环依赖就是循环引用,在spring中,就是两个或者多个bean相互之间持有对方。如下图,ClassA引用ClassB,ClassB引用ClassC,ClassC又引用ClassA,最终它们形成了一个环,这就是循环依赖。

circle

Spring中的循环依赖

spring中将循环依赖分成了3中情况,分别是:

  • 构造器循环依赖
  • prototype范围的依赖处理
  • setter循环依赖

构造器循环依赖

通过构造器注入构成的循环依赖,此依赖无法解决。在Spring中会抛出BeanCurrentlyInCreationException异常表示循环依赖。

对于构造器注入构成的循环依赖,在创建ClassA的时候,构造器需要ClassB,然后去创建ClassB,在创建ClassB的时候发现需要ClassA,形成了一个死循环,无法完成创建。

prototype范围的依赖处理

对于prototype作用域的bean,spring容器无法完成依赖注入,因为spring不像缓存单例那样缓存prototype作用域的bean。

setter循环依赖

表示通过setter注入方式构成的循环依赖,spring通过提前暴露构造器注入但未完成其他步骤(如setter操作)的bean来完成setter注入造成的循环依赖。

自己简单的用代码来展示spring解决单例setter循环依赖的方式,具体spring中如何解决感兴趣可以自己阅读源码。

创建两个循环依赖的类,ClassA和ClassB。

package io.github.brightloong.lab.spring.cyclicdependence;

/**
 * @author BrightLoong
 * @date 2018/9/13 11:17
 * @description
 */
public class ClassA {

    private ClassB classB;

    public ClassB getClassB() {
        return classB;
    }

    public void setClassB(ClassB classB) {
        this.classB = classB;
    }

    public void say() {
        System.out.println("I am ClassA");
    }
}

package io.github.brightloong.lab.spring.cyclicdependence;

/**
 * @author BrightLoong
 * @date 2018/9/13 11:17
 * @description
 */
public class ClassB {

    private ClassA classA;

    public ClassA getClassA() {
        return classA;
    }

    public void setClassA(ClassA classA) {
        this.classA = classA;
    }

    public void say() {
        System.out.println("I am ClassB. Who are you?");
        classA.say();
    }
}

ObjectFactory用来模仿Spring解决循环依赖获取bean

package io.github.brightloong.lab.spring.cyclicdependence;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author BrightLoong
 * @date 2018/9/13 11:19
 * @description
 */
public class ObjectFactory {

    /**用于缓存正在初始化中的对象,同时作为提前暴露的缓存*/
    private static final Map<Class, Object> currentInitObjects = new ConcurrentHashMap<>();

    /**用于缓存初始化好的单例对象*/
    private static final Map<Class, Object> objects = new ConcurrentHashMap<>();

    /**
     * 获取对象,并设值对象属性。
     * 1. 不考虑并发问题,简单的示例
     * 2. 解决单例setter循环依赖
     *
     * @param cls
     * @param <T>
     * @return
     */
    public <T> T getObject(Class<T> cls) {
        //如果已经初始化过直接返回
        if (objects.containsKey(cls)) {
            return (T) objects.get(cls);
        }
        try {
            T t;
            //1. 简单的使用构造函数创建对象,并提前暴露到currentInitObjects中
            t = cls.newInstance();
            //提前暴露到currentInitObjects中
            currentInitObjects.put(cls, t);
            //2. 解决依赖属性值
            resolveDependence(t, cls);
            //3. 放入单例缓存中
            objects.put(cls, t);
            return t;
        } catch (Exception e) {
            System.out.println("初始化对象失败:" + cls);
            return null;
        } finally {
            //4. 从正在初始化缓存中移除
            currentInitObjects.remove(cls);
        }
    }

    /**
     * 解决依赖属性设值.
     * @param object 对象
     * @param cls 对象class
     */
    private void resolveDependence(Object object, Class cls) {
        //获取对象的属性,并进行赋值,省去了复杂的判断,就认为是对象

        //1.获取所有属性
        Field[] fields = cls.getDeclaredFields();

        //2.循环处理属性值
        Arrays.stream(fields).forEach(field -> {
            field.setAccessible(true);
            //2.1 获取属性class属性
            Class fieldClass = field.getType();
            Object value;
            //2.2 判断是否已经初始化过
            if (objects.containsKey(fieldClass)) {
                value = objects.get(fieldClass);
            } else if (currentInitObjects.containsKey(fieldClass)) {
                //2.3 判断当前初始化的类中有没有这个属性.
                value = currentInitObjects.get(fieldClass);
            } else {
                //2.4 如果都没有,进行初始化
                value = getObject(fieldClass);
            }
            //3. 使用反射设置属性的值
            try {
                field.set(object, value);
            } catch (IllegalAccessException e) {
                System.out.println("设置对象属性失败:" + cls + "-" + field.getName());
            }
        });
    }
}

客户端调用

package io.github.brightloong.lab.spring.cyclicdependence;

/**
 * @author BrightLoong
 * @date 2018/9/13 11:19
 * @description
 */
public class Client {

    public static void main(String[] args) {
        ObjectFactory factory = new ObjectFactory();
        ClassB classB = factory.getObject(ClassB.class);
        classB.say();
        System.out.println("-----我是分割线-----");

        ClassA classA = factory.getObject(ClassA.class);
        classA.say();
        System.out.println("classB.getClassA() == classA:" + (classB.getClassA() == classA));

        System.out.println("classA.getClassB() == classB:" + (classA.getClassB() == classB));
    }
}

输出如下:

I am ClassB. Who are you?
I am ClassA
-----我是分割线-----
I am ClassA
classB.getClassA() == classA:true
classA.getClassB() == classB:true

从输出可以发现:

  • ClassA和ClassB都成功实例化
  • 都是单例

相关文章

  • Spring 是如何解决循环依赖的?

    Spring 是如何解决循环依赖的? 循环依赖: Spring 循环依赖有三种情况: 构造器的循环依赖,这种依赖 ...

  • Spring-IOC-循环依赖检测与Bean的创建

    Spring容器的循环依赖检测 Spring容器循环依赖包括:构造器循环依赖和setter循环依赖。 1- 构造器...

  • Spring之循环依赖及解决方式

    1.Spring循环依赖 循环依赖指Spring对象之间的循环引用,最终形成死循环。举例: A依赖于B,B依赖于C...

  • spring循环依赖

    Spring 循环依赖 什么是循环依赖 概念: 在代码中对象A依赖对象B,对象B依赖对象A,对象相互之间依赖关系...

  • Spring核心之bean生命周期和三级缓存

    在使用spring框架的日常开发中,bean之间的循环依赖太频繁了,spring已经帮我们去解决循环依赖问题,对我...

  • Spring中的循环依赖

    什么是循环依赖 循环依赖就是循环引用,在spring中,就是两个或者多个bean相互之间持有对方。如下图,Clas...

  • Spring中的循环依赖

    1. 什么是循环依赖 当一个bean A依赖于另一个bean B,并且bean B也依赖于bean A时,就会发生...

  • Spring中的循环依赖

    循环依赖   Spring使用依赖注入(DI)来实现控制反转(IoC),因而不可避免的会存在循环依赖的情况:当容器...

  • Spring中的循环依赖

    首先看一下出问题的代码片段: 这两段代码中定义了两个Service类:TestService1和TestServi...

  • Spring循环依赖问题

    什么是循环依赖 循环依赖就是两个对象之间存在相互依赖的关系; Spring是如何解决循环依赖的 Spring使用三...

网友评论

  • Java耕耘者:你好!我们是Java耕耘者专注于程序员Java开发公众号“Java这点事”。我们很赞赏你的文章,希望能获得转载授权。授权后,你的文章将会在公众号“Java这点事”、发布。我们会注明来源和作者姓名。
    非常感谢~~~
    Java耕耘者:@BrightLoong 好的 谢谢
    BrightLoong:@Java耕耘者 非常荣幸能被赞赏:blush::blush:,可以转载的

本文标题:Spring中的循环依赖

本文链接:https://www.haomeiwen.com/subject/gcsfnftx.html