Spring解决循环依赖问题及三级缓存的作用

下面是对于“Spring解决循环依赖问题及三级缓存的作用”的详细讲解:

一、什么是循环依赖问题?

在Spring中循环依赖是指两个或多个Bean互相依赖而形成的闭环,这样的循环依赖问题会导致Bean不能正确地完成依赖注入过程,从而导致应用程序启动失败。在依赖注入时,如果两个Bean之间相互依赖,但它们两个都没在容器中加载完成,那么就会出现循环引用的问题。例如,两个Bean有相互依赖的关系,当创建Bean A时,需要Bean B的某些属性值,而当创建Bean B时,也需要Bean A的某些属性值,这时就会形成循环依赖问题。

Spring提供了两种解决循环依赖的机制:通过BeanPostProcessor解决、使用三级缓存解决。

二、如何使用BeanPostProcessor解决循环依赖问题?

  1. 关于BeanPostProcessor

BeanPostProcessor是Spring框架中新增的特殊方法调用机制,它允许在BeanFactory组装Bean的过程中尽早的拦截到每个Bean,并进行一些增强或者替换,是SpringAOP的核心之一。

通过自定义BeanPostProcessor,可以在bean中指定需要解决的循环依赖,从而避免环依赖导致的问题。

  1. 示例说明

  2. 添加两个类:A和B

public class A {
    public B b;

    public void run() {
        ......
    }
}
public class B {
    public A a;

    public void run() {
        ......
    }
}
  • 解决方案

定义一个MyBeanPostProcessor,对bean进行特殊处理,解决循环依赖问题。

public class MyBeanPostProcessor implements BeanPostProcessor {
    private Map<String, Object> beanMap = new ConcurrentHashMap<>();

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        beanMap.put(beanName, bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //1.先要判断当前的bean是否依赖了其他bean
        if (bean instanceof A) {
            A a = (A) bean;
            if (a.b == null){
                a.b = (B) beanMap.get("B");
            }
        } else if (bean instanceof B) {
            B b = (B) bean;
            if (b.a == null) {
                b.a = (A) beanMap.get("A");
            }
        }
        return bean;
    }
}

三、如何使用三级缓存解决循环依赖问题?

  1. 关于三级缓存

Spring容器初始化时,会先将BeanDefinition缓存到BeanDefinitionMap中,然后再把实例对象缓存到singletonObjects中,这就是Spring IOC容器的一级缓存和二级缓存,而三级缓存则是用来解决循环依赖问题的。

  1. 示例说明

  2. 添加两个类:C和D

public class C {
    public D d;

    public void run() {
        ......
    }
}
public class D {
    public C c;

    public void run() {
        ......
    }
}
  • 解决方案

在创建Bean时,当发现Bean正在创建中,则会将当前正在创建的Bean对象缓存到“三级缓存”中,等待整个Bean创建完毕后再进行属性注入。

public class MyClassPathXmlApplicationContext extends AbstractApplicationContext {
  private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

  //...
  private final Map<String, Object> singletonFactories = new ConcurrentHashMap<>(256);
  private final Map<String, Object> earlySingletonObjects = new HashMap<>(256);
  private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

  //...
  private void doCreateBean(String beanName, BeanDefinition beanDefinition) {
        Object singletonObject = getSingleton(beanName);
        if (singletonObject != null) {
            return singletonObject;
        }
        //...
        createBeanInstance(beanName, beanDefinition);
        //...
        this.factoryBeanObjectCache.put(beanName, new Object[]{mbd, bean});
    }

    protected Object createBeanInstance(String beanName, BeanDefinition beanDefinition) {
        Object bean;
        try {
            //创建Bean实例
            bean = beanDefinition.getBeanClass().newInstance();
        } catch (Exception e) {
            //...
        }
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, bean));
        //...
        return bean;
    }

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.isTrue(!this.singletonFactories.containsKey(beanName), "bean factory already registered for bean \"" + beanName + "\"");
        //缓存三级缓存
        this.earlySingletonObjects.put(beanName, singletonFactory);
    }

    protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) {
        Object exposedObject = bean;
        String[] dependsOn = beanDefinition.getDependsOn();
        if (dependsOn != null) {
            for (String dependsOnBean : dependsOn) {
                if (isDependent(beanName, dependsOnBean)) {
                    registerDependentBean(dependsOnBean, beanName);
                }
                getBean(dependsOnBean);
            }
        }
        return exposedObject;
    }
  //...
}

以上就是通过三级缓存解决循环依赖问题的过程示例。

希望以上的详细讲解能够对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring解决循环依赖问题及三级缓存的作用 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • Python封装数据库连接池详解

    以下是Python封装数据库连接池的完整攻略,包含两个示例说明: 1. 安装数据库连接池库 首先,确保已经安装了Python的数据库连接池库,例如pymysql或psycopg2。可以使用以下命令进行安装: pip install pymysql 2. 封装数据库连接池类 接下来,我们需要封装一个数据库连接池类,用于管理数据库连接的创建和释放。以下是一个示例…

    other 2023年10月18日
    00
  • java-java通用继承“uncheckedcast”

    以下是关于“Java通用继承uncheckedcast”的完整攻略,包括基本概念、步骤和两个示例说明。 基本概念 Java通用继承uncheckedcast是一种类型转换操作,用于将一个对象转换为另一种类型的对象。通用继承uncheckedcast是一种不安全的类型转换,因为它不会进行类型检查,可能会导致运行时异常。通用继承uncheckedcast通常用于…

    other 2023年5月7日
    00
  • linux或windows上实现端口映射

    以下是在Linux或Windows上实现端口映射的完整攻略: 端口映射 端口映射是将一个计算机网络的端口号映射到另一个网络的端口号的过程。常用于将公共IP地址映射到私有网络中的设备上,或将外部网络中的端口映射到内部网络中的口上。 在Linux上实现端口映射 在Linux上,您可以使用iptables命令实现端口映射。以下是实现端口射的步骤: 打开终端并输入以…

    other 2023年5月7日
    00
  • 你知道怎么基于 React 封装一个组件吗

    当基于React封装组件时,需要注意以下几个步骤: 分析组件功能和逻辑,确定组件的props和state。 将组件拆分成更小的组件(如果需要)。 选择合适的生命周期方法来管理组件的行为。 确定组件样式并引入CSS样式表。 测试和调试组件。 以下是两个示例说明: 示例一: 创建一个计数器组件 确定计数器组件的props和state。我们需要一个“count”状…

    other 2023年6月25日
    00
  • macbrew安装使用卸载

    MacBrew安装使用卸载 介绍 MacBrew是苹果系统上的一种包管理工具,可以快速、简单地安装、升级和管理各种软件包,包括命令行工具、开发库、Web服务等。它使用简单,管理方便,广泛用于Mac开发者和运维人员之间,是一个非常实用的软件管理工具。 安装 1.安装Homebrew 在终端中输入以下命令: /usr/bin/ruby -e “$(curl -f…

    其他 2023年3月29日
    00
  • 在IDEA2020.2中配置使用Git的详细教程

    下面是在 IDEA2020.2 中配置使用 Git 的详细教程攻略: 前置条件 在开始 Git 的配置过程之前,请确保已经安装了 Git 工具,并且确保本地电脑上已经可以使用 Git 命令行。 配置 Git 的用户名和邮箱地址 在 IDEA 中使用 Git 前,需要配置全局用户信息,以便 Git 识别用户身份。在 Git 安装完成后,可以通过以下命令配置用户…

    other 2023年6月20日
    00
  • PostgreSQL树形结构的递归查询示例

    下面我将详细讲解如何实现PostgreSQL树形结构的递归查询。 创建样例数据表 首先,我们需要创建一个样例数据表来演示如何进行递归查询。表结构如下: CREATE TABLE category( id SERIAL PRIMARY KEY, name TEXT NOT NULL, parent_id INTEGER REFERENCES category(…

    other 2023年6月27日
    00
  • 基于HTML5 FileSystem API的使用介绍

    基于 HTML5 FileSystem API 的使用介绍 简介 HTML5 FileSystem API 允许web应用程序访问本地文件系统,从而增强了web应用程序的功能。这个API不需要服务器端的帮助,可以让用户本地文件系统的访问变得十分简单。 HTML5 FileSystem API通过两个对象的集合来实现:FileWriter和FileReader…

    other 2023年6月28日
    00
合作推广
合作推广
分享本页
返回顶部