当前位置: 首页 > wzjs >正文

大型网站的制作百度指数专业版app

大型网站的制作,百度指数专业版app,安阳县交易中心网站建设招标,沧州做网站哪家好一、什么是循环依赖 循环依赖是指两个或多个Bean之间相互依赖,形成一个闭环。例如: Bean A 依赖 Bean BBean B 依赖 Bean A 示意图如下: 在大型应用中,还可能出现更复杂的循环依赖: Bean A 依赖 Bean BBean B 依赖…

一、什么是循环依赖

循环依赖是指两个或多个Bean之间相互依赖,形成一个闭环。例如:

  • Bean A 依赖 Bean B
  • Bean B 依赖 Bean A

示意图如下:

在大型应用中,还可能出现更复杂的循环依赖:

  • Bean A 依赖 Bean B
  • Bean B 依赖 Bean C
  • Bean C 依赖 Bean A

二、为什么循环依赖是个问题

在普通的对象创建过程中,循环依赖会导致类似"鸡生蛋,蛋生鸡"的问题:

  1. 创建A时,发现依赖B,转而去创建B
  2. 创建B时,发现依赖A,但A还未创建完成
  3. 系统陷入死循环

在没有特殊处理的情况下,这将导致应用启动失败。

三、Spring如何解决循环依赖

Spring通过巧妙的设计,采用了三级缓存机制来解决循环依赖问题。这一机制主要针对单例作用域(singleton scope)的Bean。

注意:Spring无法解决以下循环依赖场景:

  • 构造器注入的循环依赖:
  1. 实例化与初始化顺序问题:Spring解决循环依赖的关键在于先实例化对象,再进行属性注入。但构造器注入是在实例化阶段就需要依赖对象,这导致了"鸡蛋-鸡"的悖论。
  2. 三级缓存机制失效:三级缓存机制依赖于先创建实例再暴露早期引用,但构造器注入需要在实例创建时就提供依赖,此时还没有可暴露的早期引用。
  • 原型模式(prototype)的循环依赖:
  1. 缓存机制不适用:三级缓存机制只应用于单例Bean。原型Bean每次获取都会创建新实例,不会被缓存。
  2. 生命周期管理差异:单例Bean由容器全程管理,而原型Bean创建后由调用者管理,Spring无法跟踪其完整生命周期。
  • 作用域为request、session等的循环依赖:
  1. 生命周期绑定:这些作用域的Bean生命周期与HTTP请求或会话绑定,不是应用级别的单例。
  2. 延迟创建特性:这些作用域的Bean通常使用代理模式延迟创建,真正的Bean实例是在请求或会话开始时才创建。
  3. 缓存隔离:每个请求/会话有自己的Bean实例,不同请求/会话间的Bean无法共享,三级缓存在这种情况下无法正常工作。

 

四、三级缓存详解

Spring的三级缓存定义在DefaultSingletonBeanRegistry类中:

/** 一级缓存:完成初始化的单例Bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/** 二级缓存:提前暴露的单例对象,尚未完成初始化 */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);/** 三级缓存:单例工厂对象,用于创建Bean并进行AOP处理 */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

各级缓存的作用

  1. 一级缓存(singletonObjects)

    • 存放完全初始化好的Bean
    • Bean已经完成属性注入和初始化方法(如initMethod)的调用
    • 可以直接使用
  2. 二级缓存(earlySingletonObjects)

    • 存放原始Bean对象,尚未完成属性注入和初始化
    • 用于解决循环依赖场景下的对象引用问题
  3. 三级缓存(singletonFactories)

    • 存放Bean的工厂对象
    • 主要作用是处理AOP代理对象的创建
    • 如果没有AOP,三级缓存可以被二级缓存替代

五、循环依赖解决流程

以A、B两个Bean循环依赖为例,Spring解决流程如下:

  1. 创建A的过程

    • 实例化A对象(此时尚未属性注入)
    • 将A的创建工厂放入三级缓存
    • 进行属性注入,发现依赖B
  2. 创建B的过程

    • 实例化B对象
    • 将B的创建工厂放入三级缓存
    • 进行属性注入,发现依赖A
    • 尝试从一级缓存获取A,失败
    • 尝试从二级缓存获取A,失败
    • 从三级缓存获取A的工厂,并使用工厂创建A的早期引用
    • 将A的早期引用从三级缓存升级到二级缓存
    • B完成属性注入(注入A的早期引用)
    • B完成初始化,放入一级缓存
  3. 完成A的创建

    • 继续A的属性注入(注入已创建完成的B)
    • A完成初始化,放入一级缓存

六、源码解析

获取Bean的主要流程

Spring在AbstractBeanFactory.doGetBean()方法中获取Bean:

protected <T> T doGetBean(String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly) {// 尝试从缓存获取Object sharedInstance = getSingleton(beanName);// 如果缓存中没有,则创建Beanif (sharedInstance == null) {// 创建Bean的过程sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);} catch (BeansException ex) {// ...}});}// ...
}

三级缓存的核心实现

DefaultSingletonBeanRegistry.getSingleton()方法展示了三级缓存的使用:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 一级缓存查找Object singletonObject = this.singletonObjects.get(beanName);// 如果一级缓存没有,且当前Bean正在创建中if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 二级缓存查找singletonObject = this.earlySingletonObjects.get(beanName);// 如果二级缓存没有,且允许早期引用if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// 三级缓存查找ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 使用工厂创建对象singletonObject = singletonFactory.getObject();// 放入二级缓存this.earlySingletonObjects.put(beanName, singletonObject);// 从三级缓存移除this.singletonFactories.remove(beanName);}}}}return singletonObject;
}

添加到三级缓存的关键代码

AbstractAutowireCapableBeanFactory.doCreateBean()方法中:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args) {// 实例化BeanBeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);final Object bean = instanceWrapper.getWrappedInstance();// 是否需要提前暴露(解决循环依赖)boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {// 添加到三级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// 属性注入和初始化populateBean(beanName, mbd, instanceWrapper);Object exposedObject = initializeBean(beanName, exposedObject, mbd);// ...
}

七、为什么需要三级缓存?

很多人疑惑:如果只是解决循环依赖,为什么需要三级缓存?二级缓存不够吗?

答案与Spring的AOP机制有关:

  1. 没有AOP时:二级缓存足够解决循环依赖

    • 一级缓存存完整Bean
    • 二级缓存存早期Bean
  2. 有AOP时:需要三级缓存

    • Spring不确定哪些Bean需要代理,只能在Bean生命周期后期才能确定
    • 三级缓存中的ObjectFactory提供了一个延迟处理的机制
    • 当出现循环依赖时,通过ObjectFactory.getObject()提前进行AOP代理
    • 确保注入的始终是符合预期的对象(原始对象或代理对象)

示例分析

假设有两个类:

@Component
public class A {@Autowiredprivate B b;public void methodA() {System.out.println("Method A");}
}@Component
public class B {@Autowiredprivate A a;public void methodB() {System.out.println("Method B");}
}

循环依赖解决过程:

  1. Spring扫描注册Bean定义
  2. 实例化Bean A
  3. A实例放入三级缓存
  4. 给A注入属性B,发现B未创建
  5. 转而实例化B
  6. B实例放入三级缓存
  7. 给B注入属性A,从三级缓存获取A,升级到二级缓存
  8. B完成初始化,放入一级缓存
  9. 继续A的注入和初始化,放入一级缓存
  10. 循环依赖解决

八、总结

Spring的三级缓存机制是一个精巧的设计,它不仅解决了循环依赖问题,还保证了AOP代理对象的正确创建与注入。三级缓存各司其职:

  • 一级缓存:存储完全初始化的Bean
  • 二级缓存:存储早期暴露的Bean
  • 三级缓存:处理AOP代理对象的创建

通过这种机制,Spring成功解决了单例Bean的循环依赖问题,为构建复杂应用提供了支持。

需要注意的是,Spring并不能解决所有循环依赖场景,如构造器注入的循环依赖和prototype作用域的循环依赖,这些情况下仍需要开发者通过调整设计来避免循环依赖。

http://www.dtcms.com/wzjs/314247.html

相关文章:

  • 网站开发所需要的的环境百度热门排行榜
  • 献县网站建设价格网络推广都有哪些方式
  • 宝安做棋牌网站建设找哪家效益快高质量发展服务业
  • 深圳住房建设网站商丘 峰少 seo博客
  • 做高清图的网站商业推广软文范例
  • 自动推广软件seo包年服务
  • 百度怎么做网站域名sem投放
  • 自己做书画交易网站网站收录查询
  • 三网合一网站建设程序照片查询百度图片搜索
  • 深圳网站建设如何制作长沙本地推广
  • 江苏城乡住房建设厅网站发外链的网址
  • 台州建设局网站培训心得体会范文大全2000字
  • wordpress 附件搜索河南智能seo快速排名软件
  • 多语言外贸企业网站源码郑州seo服务技术
  • 网站开发建设合同书沈阳百度快照优化公司
  • 做视频播放网站 赚钱新手做网络销售难吗
  • 学seo可以做网站吗郑州网络运营培训
  • python 直播网站开发站长工具怎么关掉
  • 刚开今天新开传奇网站网站建设开发价格
  • 容桂网站建设百度推广费用一天多少钱
  • 青岛网站建设有哪些公司企业文化建设
  • 朝阳网络公司北京seo网络推广
  • 网站外链如何建设东莞网站营销
  • 在网站用什么做页面布局谷歌seo顾问
  • 网站代码编辑器关键词调词平台哪个好
  • 我想看b站直播间游客怎么看网站怎么提升关键词排名
  • 网站制作 沈阳windows优化大师的作用
  • 湖南人文科技学院全国排名百度关键词seo外包
  • 网站建设PHP开发是什么意思网站不收录怎么解决
  • 南昌企业网站制作网站推广是什么