spring依赖注入——循环依赖

  • 时间:2020-04-24 21:03 作者:端吉 来源: 阅读:722
  • 扫一扫,手机访问
摘要:上一篇博客简单地分析了下依赖注入。但是对于依赖注入的很多细节,都没有深入的分析。这一篇博客会继续分析spring的依赖注入。这篇博客会处理分析getBean缓存时候遗留下来的循环依赖问题。循环依赖分析首先明确下,只有单例情况下,spring才会试着去处理循环依赖问题,多例是不会去处理循环依赖的。这个

上一篇博客简单地分析了下依赖注入。但是对于依赖注入的很多细节,都没有深入的分析。这一篇博客会继续分析spring的依赖注入。这篇博客会处理分析getBean缓存时候遗留下来的循环依赖问题。

循环依赖分析

首先明确下,只有单例情况下,spring才会试着去处理循环依赖问题,多例是不会去处理循环依赖的。这个也好了解,假如是多例的话,比方a -> b 并且 b -> a 那么,当A a=new A(); 之后要注入b,b却是多例的,那么到底该注入哪个B是不确定的。如下图:


接下来我们分析,为啥会有循环依赖的问题。

先来分析没有循环依赖的问题

public static class A{        private B b;    //省略get和set方法}public static class B{}

这个时候,假如spring先初始化A,而后会发现A依赖于B,而后就会初始化B,最后注入到A里。

整个流程用代码表示大概如下所示:

A a = 创立AB b = 创立B        -----> 创立A子流程  a.setB(b);

但是假设B也依赖了A呢?即

public static class B{        private A a;}

那样依赖注入过程就会变成(简单示例)

A a = 创立AB b = 创立B       ---->  创立B子流程 b.setA(????);  //  这个时候A还没创立完成呢a.setB(b);

那么如何处理呢?很简单,把那个还没创立完的A(只是new了,但是没有进行依赖注入的A)set到B里就好了。

弄清了这个流程之后,我们再来分析spring是如何进行依赖注入的。

spring对引用类型的注入

这里我先从spring对引用属性的注入开始。

<bean id="person" class="com.hdj.learn.spring.demo.Person">    <property name="car" ref="car"></property></bean>

即ref的注入

private Object resolveReference(Object argName, RuntimeBeanReference ref) {    try {        String refName = ref.getBeanName();        refName = String.valueOf(doEvaluate(refName));        if (ref.isToParent()) {            if (this.beanFactory.getParentBeanFactory() == null) {                throw new BeanCreationException(                        this.beanDefinition.getResourceDescription(), this.beanName,                        "Can't resolve reference to bean '" + refName +                        "' in parent factory: no parent factory available");            }            return this.beanFactory.getParentBeanFactory().getBean(refName);        }        else {            Object bean = this.beanFactory.getBean(refName);            this.beanFactory.registerDependentBean(refName, this.beanName);            return bean;        }    }    catch (BeansException ex) {        throw new BeanCreationException(                this.beanDefinition.getResourceDescription(), this.beanName,                "Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);    }}

实现很简单,就是从beanFactory里获取要依赖的对象

我们再来回顾下流程

问题是,当走到6时候,似乎又会回到1,这样不就死循环了么?

重点就是,第六步的获取A,我们回到doGetBean方法中。

getSingleton 方法

protected Object getSingleton(String beanName, boolean allowEarlyReference) {    //已创立的对象里面找下    Object singletonObject = this.singletonObjects.get(beanName);    //没找到,并且当前类正在被创立    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {        synchronized (this.singletonObjects) {            singletonObject = this.earlySingletonObjects.get(beanName);            if (singletonObject == null && allowEarlyReference) {                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);                if (singletonFactory != null) {                    singletonObject = singletonFactory.getObject();                    this.earlySingletonObjects.put(beanName, singletonObject);                    this.singletonFactories.remove(beanName);                }            }        }    }    return (singletonObject != NULL_OBJECT ? singletonObject : null);}

我们在分析初始化bean的缓存部分时,曾分析过这几个缓存。当时其实只知道了singletonObjects是存储了已经创立了的对象。

现在让我们回头再看看这些缓存。

首先看看singletonFactories赋值的地方。

doCreateBean

// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&        isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {    if (logger.isDebugEnabled()) {        logger.debug("Eagerly caching bean '" + beanName +                "' to allow for resolving potential circular references");    }    addSingletonFactory(beanName, new ObjectFactory<Object>() {        @Override        public Object getObject() throws BeansException {            //默认实现返回bean            return getEarlyBeanReference(beanName, mbd, bean);        }    });}

在创立bean时候,有这样一段代码,当需要进行提前暴露时候(当前创立对象单例 + 允许循环引用 + 当前类正在被创立)会调用addSingletonFactory方法

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {    Assert.notNull(singletonFactory, "Singleton factory must not be null");    synchronized (this.singletonObjects) {        if (!this.singletonObjects.containsKey(beanName)) {            this.singletonFactories.put(beanName, singletonFactory);            this.earlySingletonObjects.remove(beanName);            this.registeredSingletons.add(beanName);        }    }}

这里就碰见了我们要分析的singletonFactories,它存储了beanName -> 此处的匿名内部类singletonFactory。

singletonFactory

我们再回到getSingleton方法,以及之前绘制的图

ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {    singletonObject = singletonFactory.getObject();    this.earlySingletonObjects.put(beanName, singletonObject);    this.singletonFactories.remove(beanName);}

逻辑很简单


三级缓存

我们经常听说spring通过三级缓存处理了循环依赖,其实三级缓存非常简单。就是指我们分析过的

Map<String, Object> singletonObjects
Map<String, ObjectFactory<?>> singletonFactories
Map<String, Object> earlySingletonObjects

这里分别举这样三个例子。
A 依赖 B(B不依赖A)


一级缓存

A 依赖 B && B依赖A


三级缓存

A依赖B && B依赖A + B依赖C && C 依赖 A

二级缓存

至此所谓的三级缓存及其使用,应该就非常的清楚了。

总结下

这篇博客分析了spring依赖注入中,循环依赖的问题,分析了在spring初始化bean时,各级缓存的作用。应该算是挺清晰的了。

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】2FA验证器 验证码如何登录(2024-04-01 20:18)
【系统环境|】怎么做才能建设好外贸网站?(2023-12-20 10:05)
【系统环境|数据库】 潮玩宇宙游戏道具收集方法(2023-12-12 16:13)
【系统环境|】遥遥领先!青否数字人直播系统5.0发布,支持真人接管实时驱动!(2023-10-12 17:31)
【系统环境|服务器应用】克隆自己的数字人形象需要几步?(2023-09-20 17:13)
【系统环境|】Tiktok登录教程(2023-02-13 14:17)
【系统环境|】ZORRO佐罗软件安装教程及一键新机使用方法详细简介(2023-02-10 21:56)
【系统环境|】阿里云 centos 云盘扩容命令(2023-01-10 16:35)
【系统环境|】补单系统搭建补单源码搭建(2022-05-18 11:35)
【系统环境|服务器应用】高端显卡再度登上热搜,竟然是因为“断崖式”的降价(2022-04-12 19:47)
手机二维码手机访问领取大礼包
返回顶部