Spring循环依赖源码解析

一文详解 Spring Bean 循环依赖 本文主要梳理了Spring解决bean循环依赖的思路。 阅读详情

上篇文章中我们分析完了Spring中Bean的实例化过程,但是没有对循环依赖的问题进行分析,这篇文章中我们来看一下spring是如何解决循环依赖的实现。

之前在讲spring的过程中,我们提到了一个spring的单例池singletonObjects,用于存放创建好的bean,也提到过这个Map也可以说是狭义上的spring容器。

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

其实spring在缓存bean的过程中并不是只有这一个Map,我们看一下DefaultSingletonBeanRegistry这个类,在其中其实存在3个Map,这也就是经常提到的spring三级缓存。

/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

从上到下分别为一到三级缓存,这里先对三级缓存有一个初步的认识,后面使用到的时候我们再详细分析。

1、循环依赖实现流程

下面开始分析spring循环依赖的注入实现过程。先写两个bean,在它们中分别注入了对方:

@Component
public class ServiceA {
  @Autowired
  ServiceB serviceB;  
  
  public ServiceB getServiceB() {
    System.out.println("get ServiceB");
    return serviceB;
  }
}
@Component
public class ServiceB {
  @Autowired
  ServiceA serviceA;  
  
  public ServiceA getServiceA() {
    return serviceA;
  }
}

进行测试,分别调用它们的get方法,能够正常获得bean,说明循环依赖是可以实现的:

com.hydra.service.ServiceB@58fdd99
com.hydra.service.ServiceA@6b1274d2

首先,回顾一下上篇文章中讲过的bean实例化的流程。(下面的内容较多依赖于spring的bean实例化源码,如果不熟悉建议花点时间阅读一下上篇文章)

在AbstractAutowireCapableBeanFactory的doCreateBean方法中,调用createBeanInstance方法创建一个原生对象,之后调用populateBean方法执行属性的填充,最后调用各种回调方法和后置处理器。

但是在执行populateBean方法前,上篇文章中省略了一些涉及到循环依赖的内容,看一下下面这段代码:

上面的代码先进行判断:如果当前创建的是单例bean,并且允许循环依赖,并且处于创建过程中,那么执行下面的addSingletonFactory方法。


主要工作为将lambda表达式代表的ObjectFactory,放入三级缓存的Map中。注意这里只是一个存放的操作,并没有实际执行lambda表达式中的内容,具体调用过程是在后面调用ObjectFactory的getObject方法时调用。这个方法执行完成后,三级缓存中存放了一条serviceA的数据,二级缓存仍然为空。

回到正常调用流程,生成原生对象后,调用populateBean方法进行属性的赋值也就是依赖注入,具体是通过执行AutowiredAnnotationBeanPostProcessor这一后置处理器的postProcessPropertyValues方法。

在这一过程中,serviceA会找到它依赖的serviceB这一属性,当发现依赖后,会调用DefaultListableBeanFactory的doResolveDependency方法,之后执行resolveCandidate方法,在该方法中,尝试使用beanFactory获取到serviceB的bean实例。

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
      throws BeansException {
    return beanFactory.getBean(beanName);
  }

这时和之前没有循环依赖时的情况就会有些不一样了,因为现在serviceB还没有被创建出来,所以通过beanFactory是无法直接获取的。因此当在doGetBean方法中调用getSingleton方法会返回一个null值:

因此,继续使用与之前相同的创建bean的流程,实例化serviceB的bean对象。当serviceB的原生对象被实例化完成后,同样可以看到它依赖的serviceA还没有被赋值:

创建完serviceB的原生对象后,同样执行addSingletonFactory方法,将serviceB放入三级缓存中,执行完成后,三级缓存中就已经存在了两个bean的缓存:

向下执行,serviceB会调用populateBean方法进行属性填充。和之前serviceA依赖serviceB相同的调用链,执行到resolveCandidate方法,尝试使用beanFactory的getBean去获取serviceA。

向下执行,调用getSingleton方法尝试直接获取serviceA,此时三级缓存singletonFactories中我们之前已经存进去了一个key为serviceA的beanName,value为lambda表达式,这时可以直接获取到。


在执行singletonFactory的getObject方法时才去真正执行lambda表达式中的方法,实际执行的是getEarlyBeanReference方法:

在遍历后置处理器后,获取到serviceA的执行过后置处理器后的对象,执行:

this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);

这里将serviceA放入二级缓存earlySingletonObjects,并从三级缓存singletonFactories中移除。在这一步执行完后,三级缓存中的serviceA就没有了。

当我们从缓存中获取了serviceA的bean后,就不会再调用createBean去重复创建新的bean了。之后,顺调用链返回serviceB调用的doResolveDependency方法:

serviceB就成功获取到了它的依赖的serviceA属性的bean对象,回到inject方法,使用反射给serviceA赋值成功。

回到doCreateBean的方法,可以看到serviceB的serviceA属性已经被注入了,但是serviceA中的serviceB属性还是null。说明serviceB的依赖注入已经完成,而serviceA的依赖注入还没做完。

现在我们梳理一下运行到这里的流程:

1、在serviceA填充属性过程中发现依赖了serviceB,通过beanFactory的getBean方法,尝试获取serviceB

2、serviceB不存在,执行了一遍serviceB的创建流程,填充属性时发现serviceA已经存在于三级缓存,直接注入给serviceB

可以看到,在创建serviceA的过程中发现依赖的serviceB不存在,转而去创建了serviceB,而创建serviceA的流程并没有执行完,因此在创建完serviceB后再顺调用链返回,直到doResolveDependency方法:


可以看到,需要依赖的serviceB已经被创建并返回成功,返回到inject方法,同样通过反射给serviceB赋值:

返回doCreateBean方法,可以看到serviceA和serviceB之间的循环依赖已经完成了:

这样,一个最简单的循环依赖流程就结束了。有的小伙伴可能会提出疑问,这样的话,我只需要添加一个缓存存放原生对象就够了啊,为什么还需要二级缓存和三级缓存两层结构呢?

2、AOP下循环依赖具体实现

我们看看下面的例子,前面的两个serviceA和serviceB不变,我们添加一个BeanPostProcessor :

@Component
public class MyPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("serviceA")){
            System.out.println("create new ServiceA");
            return new ServiceA();
        }
        return bean;
    }
}

运行一下,结果报错了:

Exception in thread "main" org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name 'serviceA': Bean with name 'serviceA' 
has been injected into other beans [serviceB] in its raw version as 
part of a circular reference, but has eventually been wrapped. This 
means that said other beans do not use the final version of the bean.
 This is often the result of over-eager type matching - consider 
 using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned 
 off, for example.

在分析错误之前,我们再梳理一下正常循环依赖的过程:

1、初始化原生对象serviceA,放入三级缓存

2、serviceA填充属性,发现依赖serviceB,创建依赖对象

3、创建serviceB,填充属性发现依赖serviceA,从三级缓存中找到填充

4、执行serviceB的后置处理器和回调方法,放入单例池

5、执行serviceA的后置处理器和回调方法,放入单例池

再回头看上面的错误,大意为在循环依赖中我们给serviceB注入了serviceA,但是注入之后我们又在后置处理器中对serviceA进行了包装,因此导致了serviceB中注入的和最后生成的serviceA不一致。

但是熟悉aop的同学应该知道,aop的底层也是利用后置处理器实现的啊,那么为什么aop就可以正常执行呢?我们添加一个切面横切serviceA的getServiceB方法:

@Component
@Aspect
public class MyAspect {
    @Around("execution(* com.hydra.service.ServiceA.getServiceB())")
    public void invoke(ProceedingJoinPoint pjp){
        try{
            System.out.println("execute aop around method");
            pjp.proceed();
        }catch (Throwable e){
            e.printStackTrace();
        }
    }
}

先不看运行结果,代码可以正常执行不出现异常,那么aop是怎么实现的呢?

前面的流程和不使用aop相同,我们运行到serviceB需要注入serviceA的地方,调用getSingleton方法从三级缓存中获取serviceA存储的singletonFactory,调用getEarlyBeanReference方法。在该方法中遍历执行SmartInstantiationAwareBeanPostProcessor后置处理器的getEarlyBeanReference方法:

看一下都有哪些类实现了这个方法:


在spring中,就是这个AbstractAutoProxyCreator负责实现了aop,进入getEarlyBeanReference方法:

public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
    //beanName
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    this.earlyProxyReferences.put(cacheKey, bean); 
    //产生代理对象
    return wrapIfNecessary(bean, beanName, cacheKey); 
}

earlyProxyReferences 是一个Map,用于缓存bean的原始对象,也就是执行aop之前的bean,非常重要,在后面还会用到这个Map:

Map<Object, Object> earlyProxyReferences = new ConcurrentHashMap<>(16);

记住下面这个wrapIfNecessary方法,它才是真正负责生成代理对象的方法:

上面首先解析并拿到所有的切面,调用createProxy方法创建代理对象并返回。然后回到getSingleton方法中,将serviceA加入二级缓存,并从三级缓存中移除掉。

可以看到,二级缓存中的serviceA已经是被cglib代理过的代理对象了,当然这时的serviceA还是没有属性值填充的。

那么这里又会有一个问题,我们之前讲过,在填充完属性后,会调用后置处理器中的方法,而这些方法都是基于原始对象的,而不是代理对象。

在前一篇文章中我们也讲过,在initializeBean方法中会执行后置处理器,并且正常情况下aop也是在这里完成的。那么我们就要面临一个问题,如果避免重复执行aop的过程。在initializeBean方法中:

if (mbd == null || !mbd.isSynthetic()) {
  wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

调用applyBeanPostProcessorsAfterInitialization,执行所有后置处理器的after方法:

执行AbstractAutoProxyCreator的postProcessAfterInitialization方法:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
  if (bean != null) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    if (this.earlyProxyReferences.remove(cacheKey) != bean) {
      return wrapIfNecessary(bean, beanName, cacheKey);
    }
  }
  return bean;
}

earlyProxyReferences 我们之前说过非常重要,它缓存了进行aop之前的原始对象,并且这里参数传入的Object也是原始对象。因此在这里执行remove操作的判断语句返回false,不会执行if中的语句,不会再执行一遍aop的过程。

回过头来再梳理一下,因为之前进行过循环依赖,所以提前执行了AbstractAutoProxyCreator的getEarlyBeanReference方法,执行了aop的过程,在earlyProxyReferences中缓存了原生对象。因此在循环依赖的情况下,等式成立,直接返回。而在没有循环依赖的普通情况下,earlyProxyReferences执行remove返回为null,等式不成立,正常执行aop流程。

需要注意的是,这个方法中最终返回的还是原始对象,而不是aop后的代理对象。执行到这一步,我们先看一下嵌套的状态:

对外暴露的serviceA是原始对象,依赖的serviceB已经被注入了。而serviceB中依赖的serviceA是代理对象,并且这个代理对象依赖的serviceB还没有被注入。

再次通过getSingleton获取serviceA:

这次我们通过二级缓存就可以拿到之前经过aop的代理对象,因此不用找三级缓存直接返回这个代理对象,并最终把这个代理对象添加到一级缓存单例池中。

到这,我们对三级缓存的作用做一个总结:

1、singletonObjects:单例池,缓存了经过完整生命周期的bean

2、earlySingletonObjects:缓存了提前曝光的原始对象,注意这里存的还不是bean,这里存的对象经过了aop的代理,但是没有执行属性的填充以及后置处理器方法的执行

3、singletonFactories:缓存的是ObjectFactory,主要用来去生成原始对象进行了aop之后得到的代理对象。在每个bean的生成过程中,都会提前在这里缓存一个工厂。如果没有出现循环依赖依赖这个bean,那么这个工厂不会起到作用,按照正常生命周期执行,执行完后直接把本bean放入一级缓存中。如果出现了循环依赖依赖了这个bean,没有aop的情况下直接返回原始对象,有aop的情况下返回代理对象。

全部创建流程结束,看一下结果:

我们发现,在生成的serviceA的cglib代理对象中,serviceB属性值并没有被填充,只有serviceB中serviceA的属性填充成功了。

可以看到如果使用cglib,在代理对象的target中会包裹一个原始对象,而原始对象的属性是被填充过的。

那么,如果不使用cglib代理,而使用jdk动态代理呢?我们对之前的代码进行一下改造,添加两个接口:


public interface IServiceA {
    public IServiceB getServiceB();
}
public interface IServiceB {
    public IServiceA getServiceA();
}

改造两个Service类:


@Component
public class ServiceA implements IServiceA{
    @Autowired
    private IServiceB serviceB;

    public IServiceB getServiceB() {
        System.out.println("get ServiceB");
        return this.serviceB;
    }
}

@Component
public class ServiceB implements IServiceB{
    @Autowired
    private IServiceA serviceA;

    public IServiceA getServiceA() {
        System.out.println("get ServiceA");
        return serviceA;
    }
}

执行结果:

看一下serviceA的详细信息:

同样也是在target中包裹了原生对象,并在原生对象中注入了serviceB的实例。

综上两种方法,可以看出在我们执行serviceA的getServiceB方法时,都无法正常获取到其bean对象,都会返回一个null值。那么如果非要直接获得这个serviceB应该怎么办呢?

我们可以通过反射的方式,先看cglib代理情况下:


ServiceA serviceA= (ServiceA) context.getBean("serviceA");
Field h = serviceA.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(serviceA);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
ServiceA serviceA1= (ServiceA) target;
System.out.println(serviceA1.getServiceB());

再看看jdk动态代理情况下:


IServiceA serviceA = (IServiceA) context.getBean("serviceA");
Field h=serviceA.getClass().getSuperclass().getDeclaredField("h");
h.setAccessible(true);
AopProxy aopProxy = (AopProxy) h.get(serviceA);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget();
ServiceA serviceA1= (ServiceA) target;
System.out.println(serviceA1.getServiceB());

执行结果都能获取到serviceB的实例:

对aop情况下的循环依赖进行一下总结:spring专门为了处理aop情况下的循环依赖提供了特殊的解决方案,但是不论是使用jdk动态代理还是cglib代理,都在代理对象的内部包裹了原始对象,在原始对象中才有依赖的属性。此外,如果我们使用了后置处理器对bean进行包装,循环依赖的问题还是不能解决的。

3、本文总结

最后对本文的重点进行一下总结:

1、spring通过借助三级缓存完成了循环依赖的实现,这个过程中要清楚三级缓存分别在什么场景下发挥了什么具体作用

2、产生aop情况下,调用后置处理器并将生成的代理对象提前曝光,并通过额外的一个缓存避免重复执行aop

3、二级缓存和三级缓存只有在产生循环依赖的情况下,才会真正起到作用

4、此外,除去本文中提到的通过属性的方式注入依赖的情况外,大家可能会好奇如果使用构造函数能否实现循环依赖,结果是不可以的。具体的调用过程这里不再多说,有兴趣的同学可以自己再对照源码进行一下梳理。

Spring IoC源码解析之getBean,血与泪的总结 getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } //调用真正的getBean if (isEagerInit) { getBean(beanName); } } } else {//非工厂Bean就是普通的bean getBean(beanName) 阅读详情

相关推荐

SAP S4HANA F-02报错:更正统一日记账分类账的定制设置

消息号 FINS_ACDOC_CUST201 使用FINS_CUST_CONS_CHK_P去检查公司代码和分类账的设置 提示:过账期间变式为空(分类账 E1/公司代码 7500) 检查了表FINSC_LD_CMP 显示有分类账未分配过账期间变式 解决:后台配置定义分类账和货币类型的设置把过账变式维护下 路径:财务会计→财务会计全局设置→分类账→分类账→定义分类账和货币类型的设置 选中需要维护的分类账双击左侧的“分类账的公司代码设置”,找到对应的公司代码维护上即可。

qq_45068743的博客 6448

Spring consider using ‘getBeanNamesOfType‘ with the ‘allowEagerInit‘ flag turned off, for example.

看下spring说的类,两个类之间发生循环引用了,请在一方的注入属性上添加@Lazy注解。避免循环引用。

cainiao1412的博客 1万+

QOpenGL入门教程(二)—— 第一个QOpenGLWidget程序

终于到了实际操作的环节了,这一章我们要做的是: 创建OpenGL窗口 创建并编写着色器程序 为着色器程序装配顶点数据 启动渲染管线进行绘图 第一步:创建新项目 首先,我们使用Qt创建一个常规的QWidget项目,建议不要勾选ui设计器(个人习惯,笔者不太习惯于使用ui来设计) 第二步:创建QOpenGLWidget并实现三个虚函数 QOpenGLWidget Qt5.4之前,Qt可以通过一个QGLWidget类来创建OpenGL窗口,但由于opengl的发展,在5.4版本的时候,Qt提供

ItaLink 1万+

This is often the result of over-eager type matching - consider using ‘getBeanNamesForType‘

碰到循环依赖的,把类之间的关系理清楚,看看哪些是相互引用了,把循环引用给断了。这样是比较好的

一路向前 5085

手把手教你用STM32CubeMX和HAL库驱动MT6835磁编码器(SPI读取21位角度)

本文详细介绍了如何使用STM32CubeMX和HAL库驱动MT6835磁编码器,通过SPI接口读取21位高精度角度数据。从硬件准备、CubeMX配置到HAL库驱动实现与优化,提供了完整的实战指南,帮助开发者快速掌握磁编码器的应用技巧,适用于机器人关节和精密仪器等高精度场景。

weixin_28716769的博客 439

在MDK5.37版本中添加Complier Version 5( 解决Missing Compiler Version 5 )

本文章仅作为个人学习记录,方法也是从其他博主的博客中得到的,链接如下:二、Keil——Missing Compiler Version 5 和 core_cm3.c 问题解决 - YI颗白菜 - 博客园 (cnblogs.com) 问题描述 更新最新的keil版本(MDK5.37)后,在编译以前版本的工程文件时,发现出现了非常多的报错,在网上搜索之后发现原来时编译器版本不匹配的问题,即MDK5.37中使用的是Version 6版本的编译器,而缺少Version 5,对于以前编写的工

leveling99的博客 1万+

循环依赖

问题描述:idea上将一个方法抽为公共方法,启动时候报错: This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off 说的是Bean名称重复了。检查了一番,莫名其妙没发现问题,idea没...

qq_33045443的博客 1753

Cadence OrCAD: 层次化设计 Hierarchical Design

详细介绍OrCAD的原理图层次化设计方法。

Fantasy237的博客 1万+

Spring循环依赖及三级缓存源码解析

本文基于 SpringBoot5.1.8.RELEASE 版本源码解析 Spring IOC源码解析(1) Spring IOC源码解析(2) Spring拓展点及调用顺序总结 目录1. 图例解释说明1. 循环依赖流程图2. 举例说明上图2. 源码解析1. AbstractBeanFactory.doGetBean1.1 getSingleton(beanName) (DefaultSingletonBeanRegistry)(这个我们重点关注)1.2 DefaultSingletonBeanRegi.

Java动物园的专栏 673

手把手教你用Docker快速部署开源MES系统(附避坑指南)

本文提供了一份详细的实战指南,手把手教你使用Docker容器化技术快速部署开源MES系统。内容涵盖从环境准备、项目选型、数据库配置到应用服务器优化、前端部署及生产环境监控的全流程,并附有避坑指南,旨在帮助中小制造企业IT人员和开发者低成本、高效率地构建智能制造解决方案。

f0g1h2的博客 1065

一文读懂 SAP ERP / SAP S/4HANA 里的 Price List:概念、配置、定价流程与可运行 ABAP 示例

SAP ERP/S4HANA中的Price List解析 本文系统阐述了SAP定价体系中Price List的核心概念和实现机制。Price List并非独立的价目表,而是通过条件技术(条件类型、访问序列、条件记录)与Price List Type(PLTYP)共同作用的定价维度。文章详细说明了: Price List本质上是客户主数据中的PLTYP字段,作为定价查找键使用 定价流程中条件访问序列如何利用PLTYP查找特定价格 S4HANA与ECC版本在价格维护入口的差异 实际业务中如何通过不同PLTYP实

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 660

Spring源码解析-Spring 循环依赖

⼀个或多个对象之间存在直接或间接的依赖关系,这种依赖关系构成⼀个环形调⽤,有下⾯ 3 种⽅式。我们看⼀个简单的 Demo,对标“情况 2”。@Service@Autowired@Service@Autowired这是⼀个经典的循环依赖,它能正常运⾏,后⾯我们会通过源码的⻆度,解读整体的执⾏流程。我们再回顾⼀下 3 级缓存的作⽤:⼀级缓存:为“Spring 的单例属性”⽽⽣,就是个单例池,⽤来存放已经初始化完成的单例 Bean;

qq_66627105的博客 1092

KEPServerEX物联网网关(IoT Gateway)REST客户端代理(非原创)

(中文版)资料来源于【OPC-KEPServerEX学习KEPWare交流】905485143群的【455196383】【零下一度】 一。KEPServerEX物联网网关REST客户端代理演示 在KSE中,首先构建一个带有通道/设备/标签的模拟器系统。 例如,下面是三个随机项目。 直接到IoT网关插件,添加新代理。 从类型列表中选择REST Client。 终结点...

-雲飛兄-博客 5808

Spring源码之循环依赖解析

文章目录前言示例代码源码解析 前言 写完了Spring比较重要和复杂的大点,打算写一些更接近实际应用和面试问的比较多的小点,结合具体问题来看看Spring源码是怎么解决的,不过由于是小点,可能不能兼顾到整体的流程,希望大家通过之前的博文,对Spring已经有了一个整体的把控,对流程也相对比较清楚,然后结合具体问题,对一些具体的点进行更深入的研究。 那这篇博客就来看看Spring中比较有代表性的循环依赖问题,看看Spring是怎么解决的,Spring的很多设计和原理都可以应用到我们的实际工作中,技术需要落地,

从入门到脱发 1085

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Spring6 当中的 Bean 循环依赖的详细处理方案+源码解析

Bean的循环依赖:A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你,你也依赖我。singletion 下的 set 注入下的 Bean 的循环依赖能够被解决。主要原因是:在这个 singleton 单例模式下,在Spring 容器中的 bean 对象是独一无二的对象,是唯一的一个。同志在该 singleton 单例模式下:Spring 对 Bean 的管理主要分为清晰的两个阶段。

2501_92608043的博客 922

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

spring循环依赖深度源码解析

三级缓存是咋spring的DefaultSingletenBeanRegister这个类当中// 一级缓存// 三级缓存// 二级缓存在这里最值得注意的是三级缓存,我们看一下它的类型Map,我们进入源码来看一下这是个什么东西// 函数式接口 --》 只有当调用getObject时候才会真正执行代码/***/

几许的博客 1081

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

关于Spring源码解析之循环依赖

⼀个或多个对象之间存在直接或间接的依赖关系,这种依赖关系构成⼀个环形调⽤,有下⾯ 3 种⽅式。我们看⼀个简单的 Demo,对标“情况 2”。@Service@Autowired@Service@Autowired复制代码这是⼀个经典的循环依赖,它能正常运⾏,后⾯我们会通过源码的⻆度,解读整体的执⾏流程。⼀级缓存:为“Spring 的单例属性”⽽⽣,就是个单例池,⽤来存放已经初始化完成的单例 Bean;⼆级缓存:为“解决 AOP”⽽⽣,存放的是半成品的 AOP 的单例 Bean;

Java技术攻略的博客 79

【Spring 源码解析】Spring 循环依赖解析

对于循环依赖总结一些循环依赖的原因Spring有种特性,就是假如先创建class,那么就必须先创建class的依赖这个性质导致构造器注入和原型模型抛出异常!setter注入未抛出异常的原因setter注入的时候没有依赖,无需创建对应的class。希望有所收获,加油,共勉!...

燃尽余火的博客 825

源码深度解析,Spring 如何解决循环依赖?

一个或多个对象之间存在直接或间接的依赖关系,这种依赖关系构成一个环形调用,有下面 3 种方式。我们看一个简单的 Demo,对标“情况 2”。@Service}}@Service}}这是一个经典的循环依赖,它能正常运行,后面我们会通过源码的角度,解读整体的执行流程。我们再回顾一下 3 级缓存的作用:一级缓存:为“Spring 的单例属性”而生,就是个单例池,用来存放已经初始化完成的单例 Bean;二级缓存:为“解决 AOP”而生,存放的是半成品的 AOP 的单例 Bean;

lt_xiaodou的博客 126

源码深度解析,Spring 如何解决循环依赖

一个或多个对象之间存在直接或间接的依赖关系,这种依赖关系构成一个环形调用,有下面 3 种方式。我们看一个简单的 Demo,对标“情况 2”。@Service}}@Service}}复制代码这是一个经典的循环依赖,它能正常运行,后面我们会通过源码的角度,解读整体的执行流程。为“Spring 的单例属性”而生,就是个单例池,用来存放已经初始化完成的单例 Bean;为“解决 AOP”而生,存放的是半成品的 AOP 的单例 Bean;为“打破循环”而生。

java_beautiful的博客 191

Spring源码分析十:Spring中的循环依赖深度解析

Spring源码分析十:Spring中的循环依赖深度解析

我从不打没有准备的仗! 1万+

Spring IOC源码解析-循环依赖的解决方法

一.简介 本文,我们将来看一下Spring是如何解决循环依赖问题的。在本篇文章中,首先会介绍下什么是循环依赖,然后介绍下Spring将玄幻依赖分类的情况。最后,进行源码解析。为了更好的解析Spring解决循环依赖的办法,将会从获取bean的方法getBean开始,将这个调用过程梳理一遍,然后在详细分析源码。 二.什么是循环依赖 2.1概述 循环依赖就是循环引用,就是两个或多个bean之间的...

努力找工作呀 691

【spring源码深度解析】:spring bean循环依赖

在【spring源码深度解析】一书介绍循环依赖相关内容其基础上进行了更加详细的说明并补充了我个人对spring实现方式的疑惑及理解,并绘制了流程图,详细举例说明了创建bean时循环依赖的解决流程

Fight_Rain的博客 723

人人都能看懂的Spring源码解析,Spring如何解决循环依赖

两个Bean,BeanA和BeanB,它们都有一个引用指向对方,这就是最简单的循环依赖。还有更复杂的循环依赖,涉及到多个bean,只要其中形成环,就是循环依赖。以上就是关于“Spring如何处理循环依赖”的讲解的全部内容。Spring是通过三级缓存解决循环依赖的。一级缓存是singletonObjects;二级缓存是earlySingletonObjects;三级缓存是singletonFactories。

weixin_43889578的博客 2008
上一篇: Spring实例化Bean源码解析
下一篇: Redis主从复制架构搭建
码农参上
博客等级 码龄17年 9050粉丝 77原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值