spring怎么避免循环依赖
1、循环依赖
(1)概念
对象依赖分为强依赖和弱依赖:
强依赖指的是一个对象包含了另外一个对象的引用,例如:学生类中包含了课程类,在学生类中存在课程类的引用
创建课程类:
- @Data
- public class Course {
- private String cname;
- private Integer credit;
- }
创建学生类:
- @Data
- public class Student {
- private String sname;
- private int age;
- private Course course;
- }
测试类:
- public class Test {
- public static void main(String[] args) {
- Student student=new Student();
- Course course=new Course();
- student.setCourse(course);
- }
- }
弱依赖指的是一个对象里面调用了另外一个对象
循环依赖:A引用B,而B又引用A
创建课程类,课程类中有学生类的引用:
- @Data
- public class Course {
- private String cname;
- private Integer credit;
- private Student student;
- }
创建学生类,学生类中存在课程类中的引用:
- @Data
- public class Student {
- private String sname;
- private int age;
- private Course course;
- }
创建测试类,测试类中学生类调用课程类的对象,课程类的对象又调用学生类的对象:
- public class Test {
- public static void main(String[] args) {
- Student student=new Student();
- Course course=new Course();
- student.setCourse(course);
- course.setStudent(student);
- }
- }
测试类可以正常地获取对象,也就是说这种方式是支持循环依赖的。
(2)spring的循环依赖
- <bean id="student" class="com.zhb.bean.Student" scope="singleton">
- <property name="course" ref="course"></property>
- </bean>
- <bean id="course" class="com.zhb.bean.Course" scope="singleton">
- <property name="student" ref="student"></property>
- </bean>
单例模式下支持,多例模式下不支持
(3)spring是否支持循环依赖
支持
(4)spring的所有对象是不是都支持循环依赖
单例模式下支持:单例模式下是首先创建出两个对象,然后进行依赖的注入。A注入B,B注入A。
多例模式下不支持:A创建的时候会去判断是否依赖于其他对象,如果依赖的话就不会去先创建A。创建B的时候也会去判断是否依赖其他对象,如果A依赖于B,而B又依赖于A的话就会造成一个死循环,两个对象都不会创建。
2、spring循环依赖的过程
(1)流程
(2)源码
ClassPathXmlApplicationContext方法:
- public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
- super(parent);
- this.setConfigLocations(configLocations);
- if (refresh) {
- this.refresh();//refresh方法是真正的创建对象的方法
- }
- }
refresh方法:
- public void refresh() throws BeansException, IllegalStateException {
- synchronized(this.startupShutdownMonitor) {
- this.prepareRefresh();
- ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
- this.prepareBeanFactory(beanFactory);
- try {
- this.postProcessBeanFactory(beanFactory);
- this.invokeBeanFactoryPostProcessors(beanFactory);//Bean工厂的后置处理器
- this.registerBeanPostProcessors(beanFactory);
- this.initMessageSource();
- this.initApplicationEventMulticaster();
- this.onRefresh();
- this.registerListeners();
- this.finishBeanFactoryInitialization(beanFactory);//此方法是真正的创建对象的方法,能够实例化所有的单例bean
- this.finishRefresh();
- } catch (BeansException var9) {
- if (this.logger.isWarnEnabled()) {
- this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
- }
- this.destroyBeans();
- this.cancelRefresh(var9);
- throw var9;
- } finally {
- this.resetCommonCaches();
- }
- }
- }
finishBeanFactoryInitialization
- protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
- if (beanFactory.containsBean("conversionService") && beanFactory.isTypeMatch("conversionService", ConversionService.class)) {
- beanFactory.setConversionService((ConversionService)beanFactory.getBean("conversionService", ConversionService.class));
- }
- if (!beanFactory.hasEmbeddedValueResolver()) {
- beanFactory.addEmbeddedValueResolver((strVal) -> {
- return this.getEnvironment().resolvePlaceholders(strVal);
- });
- }
- String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
- String[] var3 = weaverAwareNames;
- int var4 = weaverAwareNames.length;
- for(int var5 = 0; var5 < var4; ++var5) {
- String weaverAwareName = var3[var5];
- this.getBean(weaverAwareName);
- }
- beanFactory.setTempClassLoader((ClassLoader)null);
- beanFactory.freezeConfiguration();
- beanFactory.preInstantiateSingletons();
- }
接口:
- void preInstantiateSingletons() throws BeansException;
- public void preInstantiateSingletons() throws BeansException {
- if (this.logger.isTraceEnabled()) {
- this.logger.trace("Pre-instantiating singletons in " + this);
- }
- List<String> beanNames = new ArrayList(this.beanDefinitionNames);
- Iterator var2 = beanNames.iterator();//取出名字
- while(true) {
- String beanName;
- Object bean;
- do {
- while(true) {
- RootBeanDefinition bd;
- do {
- do {
- do {
- if (!var2.hasNext()) {
- var2 = beanNames.iterator();
- while(var2.hasNext()) {
- beanName = (String)var2.next();
- Object singletonInstance = this.getSingleton(beanName);
- if (singletonInstance instanceof SmartInitializingSingleton) {
- SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;
- if (System.getSecurityManager() != null) {
- AccessController.doPrivileged(() -> {
- smartSingleton.afterSingletonsInstantiated();
- return null;
- }, this.getAccessControlContext());
- } else {
- smartSingleton.afterSingletonsInstantiated();
- }
- }
- }
- return;
- }
- beanName = (String)var2.next();
- bd = this.getMergedLocalBeanDefinition(beanName);
- } while(bd.isAbstract());//单例抽象懒加载
- } while(!bd.isSingleton());
- } while(bd.isLazyInit());
- if (this.isFactoryBean(beanName)) {
- bean = this.getBean("&" + beanName);
- break;
- }
- this.getBean(beanName);
- }
- } while(!(bean instanceof FactoryBean));
- FactoryBean<?> factory = (FactoryBean)bean;
- boolean isEagerInit;
- if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
- SmartFactoryBean var10000 = (SmartFactoryBean)factory;
- ((SmartFactoryBean)factory).getClass();
- isEagerInit = (Boolean)AccessController.doPrivileged(var10000::isEagerInit, this.getAccessControlContext());
- } else {
- isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();
- }
- if (isEagerInit) {
- this.getBean(beanName);
- }
- }
- }
getSingleton方法:
- @Nullable
- protected Object getSingleton(String beanName, boolean allowEarlyReference) {
- Object singletonObject = this.singletonObjects.get(beanName);//
- if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
- synchronized(this.singletonObjects) {
- singletonObject = this.earlySingletonObjects.get(beanName);
- if (singletonObject == null && allowEarlyReference) {
- ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
- if (singletonFactory != null) {
- singletonObject = singletonFactory.getObject();
- this.earlySingletonObjects.put(beanName, singletonObject);
- this.singletonFactories.remove(beanName);
- }
- }
- }
- }
- return singletonObject;
- }
3、怎么避免循环依赖?
三级缓存
- 一级缓存:singletonObjects,存放完全实例化属性赋值完成的Bean,直接可以使用。
- 二级缓存:earlySingletonObjects,存放早期Bean的引用,尚未属性装配的Bean
- 三级缓存:singletonFactories,三级缓存,存放实例化完成的Bean工厂。
假设A依赖B,B依赖A(注意:这里是set属性依赖)分以下步骤执行:
A依次执行doGetBean、查询缓存、createBean创建实例,实例化完成放入三级缓存singletonFactories中,接着执行populateBean方法装配属性,但是发现有一个属性是B的对象。因此再次调用doGetBean方法创建B的实例,依次执行doGetBean、查询缓存、createBean创建实例,实例化完成之后放入三级缓存singletonFactories中,执行populateBean装配属性,但是此时发现有一个属性是A对象。
因此再次调用doGetBean创建A的实例,但是执行到getSingleton查询缓存的时候,从三级缓存中查询到了A的实例(早期引用,未完成属性装配),此时直接返回A,不用执行后续的流程创建A了,那么B就完成了属性装配,此时是一个完整的对象放入到一级缓存singletonObjects中。
B创建完成了,则A自然完成了属性装配,也创建完成放入了一级缓存singletonObjects中。
spring怎么避免循环依赖的更多相关文章
- Spring源代码解析 ---- 循环依赖
一.循环引用: 1. 定义: 循环依赖就是循环引用,就是两个或多个Bean相互之间的持有对方,比方CircularityA引用CircularityB,CircularityB引用Circularit ...
- Spring源码-循环依赖源码解读
Spring源码-循环依赖源码解读 笔者最近无论是看书还是从网上找资料,都没发现对Spring源码是怎么解决循环依赖这一问题的详解,大家都是解释了Spring解决循环依赖的想法(有的解释也不准确,在& ...
- Spring中的循环依赖解决详解
前言 说起Spring中循环依赖的解决办法,相信很多园友们都或多或少的知道一些,但当真的要详细说明的时候,可能又没法一下将它讲清楚.本文就试着尽自己所能,对此做出一个较详细的解读.另,需注意一点,下文 ...
- Spring 如何解决循环依赖问题?
在关于Spring的面试中,我们经常会被问到一个问题,就是Spring是如何解决循环依赖的问题的. 这个问题算是关于Spring的一个高频面试题,因为如果不刻意研读,相信即使读过源码,面试者也不一定能 ...
- Spring如何解决循环依赖问题
目录 1. 什么是循环依赖? 2. 怎么检测是否存在循环依赖 3. Spring怎么解决循环依赖 本文主要是分析Spring bean的循环依赖,以及Spring的解决方式. 通过这种解决方式,我们可 ...
- Spring 如何解决循环依赖的问题
Spring 如何解决循环依赖的问题 https://blog.csdn.net/qq_36381855/article/details/79752689 Spring IOC 容器源码分析 - 循环 ...
- Spring如何解决循环依赖?
介绍 先说一下什么是循环依赖,Spring在初始化A的时候需要注入B,而初始化B的时候需要注入A,在Spring启动后这2个Bean都要被初始化完成 Spring的循环依赖有两种场景 构造器的循环依赖 ...
- 面试必杀技,讲一讲Spring中的循环依赖
本系列文章: 听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configu ...
- 面试阿里,腾讯,字节跳动90%都会被问到的Spring中的循环依赖
前言 Spring中的循环依赖一直是Spring中一个很重要的话题,一方面是因为源码中为了解决循环依赖做了很多处理,另外一方面是因为面试的时候,如果问到Spring中比较高阶的问题,那么循环依赖必定逃 ...
随机推荐
- IdentityServer4网页(单点)登陆入门
前言 本篇说说ids中的网页登陆以及单点登陆的大致原理,主要是以基本跑通为目的,下一篇开始会详细说明集成ids网页登陆原理. 最好先熟悉以下知识: asp.net core asp.net core的 ...
- The Successor Representation: Its Computational Logic and Neural Substrates
郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! Received May 14, 2018; revised June 28, 2018; accepted July 5, 2018.T ...
- promise的常用情况
因为js是单线程的,所以一旦代码中有报错,就不会执行下面的了,如下333就未打印 console.log(111)throw Error(222)console.log(333) 好像与promise ...
- 七夕节来啦!AI一键生成情诗,去发给你的女朋友吧!
[摘要] Hello大家好,今天就是七夕节了,为了增进和女朋友之间的情感,我写了一个自动生成情诗的AI: 大家可以在ModelArts尝试复现模型,然后快去发给你们的女朋友吧- 大家好,我是b站up主 ...
- 洛谷 P3951 NOIP 2017 小凯的疑惑
洛谷 P3951 NOIP 2017 小凯的疑惑 题目描述 小凯手中有两种面值的金币,两种面值均为正整数且彼此互素.每种金币小凯都有 无数个.在不找零的情况下,仅凭这两种金币,有些物品他是无法准确支付 ...
- istio的安全(概念)
Istio 安全(概念) 目录 Istio 安全(概念) 高层架构 Istio身份 身份和证书管理 认证 Mutial TLS认证 宽容(Permissive)模式 安全命名 认证架构 认证策略 策略 ...
- 大数据计算的基石——MapReduce
MapReduce Google File System提供了大数据存储的方案,这也为后来HDFS提供了理论依据,但是在大数据存储之上的大数据计算则不得不提到MapReduce. 虽然现在通过框架的不 ...
- MSP430-LED中断闪烁代码详解
使用MSP430F149的开发板,首先对LED闪烁灯的例程进行讲解,然后下边是自己写的,将部分代码写入了新建的led.c程序中 #include <msp430x14x.h> ...
- Vuex 注入 Vue 生命周期的过程
首先我们结合 Vue 和 Vuex 的部分源码,来说明 Vuex 注入 Vue 生命周期的过程. 说到源码,其实没有想象的那么难.也和我们平时写业务代码差不多,都是方法的调用.但是源码的调用树会复杂很 ...
- MySql密码的问题
由于长时间没使用过MySql了,也由于之前没有做笔记的习惯,晚上因为MySQL的密码问题导致数据库长时间没连上.纠结了这么久还是决定记录下来,毕竟安装的东西多了,这年头到处都是密码,加上时间一长,很容 ...