Spring循环依赖(四)

本文详细阐述了Spring中循环依赖的原理,通过实例演示如何利用singletonObjects、earlySingletonObjects和singletonFactories三级缓存来避免循环依赖,以及AOP代理的时机选择。
摘要由CSDN通过智能技术生成

循环依赖

什么是循环依赖?

依赖:在Spring中,A类将其他的类当作属性时,就造成了A依赖其他类

定义:

​ 现有A、B两个类,如果A类将B类作为属性(并设置@Autowired),B类也将A类作为属性(并设置@Autowired),那么就造成了循环依赖 【A依赖于B,B依赖于A】

现象:

当A、B循环依赖:

  1. 创建beanA,发现依赖beanB

  2. 创建beanB,发现依赖beanA

  3. 创建beanA,发现依赖beanB

  4. 创建beanB,发现依赖beanA

    …// 循环

类型:

  • 单例bean循环依赖 —— 三级缓存解决

  • 原型bean循环依赖 —— 无法解决,直接报错

    • 因为它没有用到缓存池,所以无法解决
  • 构造器参数循环依赖 —— @Lazy注解解决

解决:

只有setter注入属性的方式可以解决,构造方法注入不能解决!【有可能导致构造器参数循环依赖

关闭循环依赖

源码:

public class Main {
    public static void main(String[] args) {
        // 1. 执行this()
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 禁止循环依赖
        applicationContext.setAllowCircularReferences(false);
        // 2. 注册配置类
        applicationContext.register(AppConfig.class);
        // 3. 刷新容器
        applicationContext.refresh();
    }
}

简单解决循环依赖

定义:

​ 在Spring中,有三个缓存池,其实用两个缓存池就可以简单的解决循环依赖问题,下面介绍用两个缓存池怎么简单解决。

缓存池组成:

缓存池作用
singletonObjects 一级缓存存放经历了 完整生命周期的bean
earlySingletonObjects 二级缓存存放 刚创建出来的、不完整的bean无属性
singletonFactories 三级缓存存放 bean对应的函数
这里的函数支持调用BeanPostProcessor实现AOP动态代理

Spring解决循环依赖流程

开始

  1. A进入getBean()方法

  2. A开始createBean()

  3. 实例化纯净的BeanA

  4. 三级缓存中拿A自己的回调方法 —— 出口

  5. 如果拿不到,将A的回调方法加入三级缓存

  6. 填充属性

    1. 填充属性发现依赖B,跑去创建beanB

      1. B进入getBean()方法

      2. B开始createBean()

      3. 实例化纯净的BeanB

      4. 三级缓存中拿B自己的回调方法

      5. 如果拿不到,将B的回调方法加入三级缓存

      6. 填充属性

        1. 填充属性发现依赖A,跑去创建beanA

          1. A进入getBean()方法

          2. A开始createBean()

          3. 实例化纯净的BeanA

          4. 三级缓存中拿A自己的回调方法,拿到了,调用回调

            1. 注意!此时是跑去了第一次A进入getBean()的时候

            2. 如果单例池、二级缓存没有A,并且有A的三级缓存函数,那么==将A加入二级缓存,并删除A的三级缓存==

            3. 调用回调,获取第3步实例化的那个纯净的BeanA

          5. 拿到BeanA了,返回给B

          6. 第二次A的 getBean() 退出

      7. B继续填充属性 【使B完整】

      8. 加入单例池,并删除二级缓存和三级缓存的B

      9. 调用初始化方法

      10. 拿到beanB了,返回给A

  7. A继续填充属性 【使A完整】

  8. 调用初始化方法

  9. 加入单例池,并删除二级缓存和三级缓存的A

结束

二级缓存和三级缓存分别解决循环依赖

  • 简易版二级缓存

  • 简易版三级缓存

A类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 循环依赖 实例A
 *
 * @author summer <995214902@qq.com>
 * @date 2022/3/18 19:23
 */
@Component
public class InstanceA {

    @Autowired
    private InstanceB instanceB;

    public InstanceA() {
        System.out.println("InstanceA实例化");
    }

}

B类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 循环依赖 实例A
 *
 * @author summer <995214902@qq.com>
 * @date 2022/3/18 19:23
 */
@Component
public class InstanceB {

    @Autowired
    private InstanceA instanceA;

    public InstanceB() {
        System.out.println("InstanceB实例化");
    }

}

简易版二级缓存解决循环依赖

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;

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


/**
 * 二级缓存解决循环依赖
 *
 * @author summer <995214902@qq.com>
 * @date 2022/3/18 19:23
 */
public class MainStart {

    // 一级缓存
    public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    // 二级缓存: 为了将 成熟Bean和纯净Bean分离,避免读取到不完整得Bean
    public static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

    // 原料map
    private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    /**
     * 读取bean定义,当然在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 {
        // 加载了BeanDefinition
        loadBeanDefinitions();

        // 1.开始啦!!! 循环创建Bean
        for (String key : beanDefinitionMap.keySet()) {
            // 调用getBean,先从A开始
            getBean(key);
        }

        InstanceA singleBeanA = (InstanceA) getBean("instanceA");
    }


    /**
     * 获取bean
     *
     * @param beanName bean名称
     * @return bean对象
     */
    public static Object getBean(String beanName) throws IllegalAccessException, InstantiationException {

        // 1. 调用getSingleton看看单例池有没有
        Object singleton = getSingleton(beanName);

        // 2. 单例池有直接返回,没有就直接往下走
        if (singleton != null) {
            return singleton;
        }

        // 4. 实例化一个bean
        RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
        Class<?> beanClass = beanDefinition.getBeanClass();
        // 5. 拿到纯净的bean
        Object instanceBean = beanClass.newInstance();


        /*
         * 如果是二级缓存,要实现AOP动态代理,就得在这里实现AOP动态代理
         * 因为循环依赖的出口要在属性赋值之前,而这个出口就是二级缓存【第六步】
         *
         * 而Spring希望AOP动态代理在bean实例化并初始化完成后,再进行代理,所以引入了三级缓存。
         */

        // 6. 添加二级缓存,解决循环依赖
        earlySingletonObjects.put(beanName,instanceBean);

        // 7. 属性赋值
        Field[] declaredFields = beanClass.getDeclaredFields();
        for (Field field : declaredFields) {
            Autowired annotation = field.getAnnotation(Autowired.class);
            // 8. 如果属性上有@Autowired,那我就给他注入
            if (annotation != null) {
                String name = field.getName();
                // 9. 递归入口获得属性
                Object fileObject = getBean(name);
                field.setAccessible(true);
                field.set(instanceBean, fileObject);
            }

        }

        // 12. 调用初始化函数
        // init-method

        // 13. 添加到一级缓存
        singletonObjects.put(beanName, instanceBean);

        // 14. remove 二级缓存
        earlySingletonObjects.remove(beanName);

        return instanceBean;
    }



    public static Object getSingleton(String beanName) {

        Object bean = singletonObjects.get(beanName);

        if (bean == null) {
            // 3. 从二级缓存中拿
            bean = earlySingletonObjects.get(beanName);
        }

        return bean;

    }

}

简易版三级缓存解决循环依赖

主流程

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 三级缓存解决循环依赖
 *
 * @author summer <995214902@qq.com>
 * @date 2022/3/18 19:23
 */
public class MainStart {

    // 一级缓存
    public static Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    // 二级缓存: 将完整bean和纯净bean分开
    public static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

    // 三级缓存:存储 bean 对应增强bean的方法 [AOP代理]
    public static Map<String, ObjectFactory> singletonFactories = new ConcurrentHashMap<>();

    // 标识正在创建的单例的set集合
    public static Set<String> singletonsCurrentlyInCreation = new HashSet<>();

    // 原料map
    private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    
    public static void main(String[] args) throws Exception {
        // 加载了BeanDefinition
        loadBeanDefinitions();

        // 1.开始啦!!! 循环创建Bean
        for (String key : beanDefinitionMap.keySet()) {
            // 调用getBean,先从A开始
            getBean(key);
        }

        InstanceA singleBeanA = (InstanceA) getBean("instanceA");
    }
    
    
    /**
     * 读取bean定义,模仿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);
    }

    
    /**
     * 获取bean
     *
     * @param beanName bean名称
     * @return bean对象
     */
    public static Object getBean(String beanName) throws Exception {

        // 1. 调用getSingleton看看单例池有没有
        Object singleton = getSingleton(beanName);

        // 2. 单例池有直接返回,没有就直接往下走
        if (singleton != null) {
            return singleton;
        }

        // 3. 将bean标识为正在创建
        singletonsCurrentlyInCreation.add(beanName);

        // 4. 实例化一个bean
        RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
        Class<?> beanClass = beanDefinition.getBeanClass();
        // 5. 拿到纯净的bean
        Object instanceBean = beanClass.newInstance();

        if (!singletonFactories.containsKey(beanName)) {
            // 6. 三级缓存拿不到回调方法,将自己的回调方法getEarlyBeanReference() 放进去
            Object finalInstanceBean = instanceBean;
            singletonFactories.put(beanName, () -> getEarlyBeanReference(finalInstanceBean, beanName));
        }

        // 7. 属性赋值
        Field[] declaredFields = beanClass.getDeclaredFields();
        for (Field field : declaredFields) {
            Autowired annotation = field.getAnnotation(Autowired.class);
            // 8. 如果属性上有@Autowired,那我就给他注入
            if (annotation != null) {
                field.setAccessible(true);
                String name = field.getName();
                // 9. 递归入口获得属性
                Object fileObject = getBean(name);
                field.set(instanceBean, fileObject);
            }

        }


        // 10. 属性赋值结束,正常情况下会在初始化之后创建proxy,就是bean有可能会被代理,所以这情况还会拿到被代理的bean
        // 11. 由于递归完后,第一次的A还是原实例,所以要从二级缓存中拿到proxy
        if (earlySingletonObjects.containsKey(beanName)) {
            instanceBean = earlySingletonObjects.get(beanName);
        }

        // 12. 调用初始化函数
        // init-method

        // 13. 添加到一级缓存
        singletonObjects.put(beanName, instanceBean);


        // 14. remove 二级缓存和三级缓存
        earlySingletonObjects.remove(beanName);
        singletonFactories.remove(beanName);

        return instanceBean;
    }

    
    /**
     * 获取bean早期引用,Spring在这里进行BeanPostProcessor的调用 来实现AOP
     *
     * @param beanName bean名称
     * @return bean对象
     */
    private static Object getEarlyBeanReference(Object finalInstanceBean, String beanName) {
        // 看beanName符合什么规范,让 BeanPostProcessor 给他创建动态代理 【开挂】
        return finalInstanceBean;
    }


    public static Object getSingleton(String beanName) {
        // 1. 先从一级缓存中拿
        Object bean = singletonObjects.get(beanName);

        // 2. 如果当前bean拿不到,只有两种情况:
        // 一、刚好是一个新的类刚刚开始创建 二、我之前已经正在创建了,又要get一次,说明是循环依赖
        if (bean == null && singletonsCurrentlyInCreation.contains(beanName)) {
            // 3. 从二级缓存中拿
            bean = earlySingletonObjects.get(beanName);
            if (bean == null) {
                // 4. 从三级缓存中拿
                ObjectFactory factory = singletonFactories.get(beanName);
                if (factory != null) {
                    // 5. 拿到被动态代理的、纯净的bean
                    bean = factory.getObject();
                    // 6. 加入二级缓存,给被依赖的bean获取 【B】
                    earlySingletonObjects.put(beanName, bean);
                }
            }


        }

        return bean;

    }

}

ObjectFactory函数接口

/**
 * 循环依赖,函数接口
 * <p>
 * 函数接口很有意思
 * <p>
 * 它可以将一个函数作为参数传递,然后在你想要调用的时机调用这个函数
 *
 * @author summer <995214902@qq.com>
 * @date 2022/3/18 19:23
 */
@FunctionalInterface
public interface ObjectFactory<T> {

    /**
     * 返回此工厂管理的对象的实例,在这里可以增强bean
     *
     * @return 结果实例
     */
    T getObject();

}

Q

为什么二级缓存就能解决循环依赖,还要三级缓存?

​ 因为Spring希望在 bean实例化并初始化之后 再去实现AOP动态代理

  • 只用两级缓存的话,AOP动态代理必须放到属性赋值之前,不然没有循环依赖出口

好处:解耦、便于实现AOP动态代理吧~

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-njvhSdau-1652175560946)(3%20%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96.assets/%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E8%AF%BE%E4%B8%8A%E5%9B%BE%E3%80%90%E6%9B%B4%E5%A4%9A%E8%AF%BE%E7%A8%8B%20zx-cc.net%E3%80%91.png)]

Spring中循环依赖解决

就是getBean()那里分析的

  1. 利用三级缓存存储 函数方法进行回调获取早期对象解决循环依赖

  2. 考虑AOP是否应该提前 【实例化后 AOP 还是 初始化后AOP】

    • 实例化后 —— getEarlyReference() 里面调用BeanPostProcessor

    • 初始化后 —— applyBeanPostProcessorsAfterInitialization() 里面调用BeanPostProcessor

就做了这两件事来解决循环依赖的问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值