Spring中的循环依赖

4651321-fe7e1031a664c2d3
circle
阅读原文请访问我的博客 BrightLoong's Blog

什么是循环依赖

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

4651321-0a82667e56473678
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都成功实例化
  • 都是单例
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值