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

只做网站的模仿大型门户网站做ppt

只做网站的,模仿大型门户网站做ppt,网站做qq链接代码,中国建设局网站Spring循环依赖详解 什么是循环依赖? 循环依赖是指两个或多个Bean之间相互依赖,形成一个循环引用的情况。例如: Service public class A {Autowiredprivate B b; }Service public class B {Autowiredprivate A a; }在上面的例子中&#xf…

Spring循环依赖详解

什么是循环依赖?

循环依赖是指两个或多个Bean之间相互依赖,形成一个循环引用的情况。例如:

@Service
public class A {@Autowiredprivate B b;
}@Service
public class B {@Autowiredprivate A a;
}

在上面的例子中,Bean A依赖Bean B,同时Bean B也依赖Bean A,这就形成了循环依赖。

Spring Bean的生命周期

在深入理解循环依赖之前,我们需要先了解Spring Bean的完整生命周期:

  1. 实例化(Instantiation)

    • 调用构造函数创建对象
    • 此时对象还未完成初始化
    • 对象处于"半成品"状态
  2. 属性赋值(Populate Properties)

    • 设置对象属性值
    • 注入依赖的其他Bean
    • 执行@Autowired等注解的注入
  3. 初始化(Initialization)

    • 调用@PostConstruct注解的方法
    • 实现InitializingBean接口的afterPropertiesSet方法
    • 执行自定义的init-method方法
    • 执行BeanPostProcessor的postProcessAfterInitialization方法
  4. 使用(In Use)

    • Bean可以被应用程序使用
    • 所有属性都已正确初始化
    • 所有初始化方法都已执行
  5. 销毁(Destruction)

    • 调用@PreDestroy注解的方法
    • 实现DisposableBean接口的destroy方法
    • 执行自定义的destroy-method方法

Spring如何解决循环依赖?

关键就是提前暴露未完全创建完毕的 Bean。

在 Spring 中主要是使用三级缓存来解决了循环依赖:

  • 一级缓存(Singleton Objects Map):用于存储完全初始化完成的单例Bean。
  • 二级缓存(Early Singleton Objects Map):用于存储尚未完全初始化,但已实例化的Bean,用于提前暴露对象,避免循环依赖问题。
  • 三级缓存(Singleton Factories Map):用于存储对象工厂,当需要时,可以通过工厂创建早期Bean(特别是为了支持AOP代理对象的创建)。

解决步骤

  • Spring 首先创建 Bean 实例,并将其加入三级缓存中(Factory)。
  • 当一个 Bean 依赖另一个未初始化的 Bean 时,Spring 会从三级缓存中获取 Bean 的工厂,并生成该 Bean 的对象(若有代理则是代理对象)。
  • 代理对象存入二级缓存,解决循环依赖。
  • 一旦所有依赖 Bean 被完全初始化,Bean 将转移到一级缓存中。

源码分析

Spring的核心实现在DefaultSingletonBeanRegistry类中:

public class DefaultSingletonBeanRegistry extends FactoryBeanRegistrySupport {// 一级缓存:完全初始化好的Beanprivate final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 二级缓存:提前曝光的Bean(未完成属性填充)private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);// 三级缓存:Bean的工厂对象private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);// 正在创建的Bean名称集合private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}

三级缓存详解

缓存的作用和特点

  1. 一级缓存(singletonObjects)

    • 存储完全初始化好的Bean
    • 可以直接被使用
    • 线程安全(ConcurrentHashMap)
    • 最终存储的Bean实例
    • 包含所有初始化完成的属性
    • 是最终可用的Bean
  2. 二级缓存(earlySingletonObjects)

    • 存储提前曝光的Bean
    • 用于解决普通Bean的循环依赖
    • 避免重复创建对象
    • 存储的是原始对象或代理对象
    • 生命周期较短,主要用于解决循环依赖
    • 是"半成品"Bean
  3. 三级缓存(singletonFactories)

    • 存储Bean的工厂对象(ObjectFactory)
    • 用于处理AOP代理对象的循环依赖
    • 可以返回代理对象或原始对象
    • 工厂对象可以动态决定返回什么类型的对象
    • 支持AOP的动态代理
    • 是创建Bean的工厂

为什么需要三级缓存?

三级缓存的存在主要是为了解决AOP代理对象的循环依赖问题。具体原因如下:

  1. AOP代理的特殊性

    • AOP代理对象需要在Bean初始化完成后才能创建
    • 代理对象需要包装原始对象
    • 代理对象的创建时机与普通Bean不同
    • 代理对象需要等待所有BeanPostProcessor执行完成
  2. 获取Bean的早期引用

    /*** 获取Bean的早期引用,用于解决循环依赖* @param beanName Bean的名称* @param mbd Bean的定义信息* @param bean 原始Bean对象* @return 可能是原始Bean或代理对象*/
    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {// 默认使用原始Bean对象Object exposedObject = bean;// 判断是否需要创建代理对象// 1. mbd.isSynthetic()为false,表示不是合成的Bean// 2. 存在InstantiationAwareBeanPostProcessor处理器if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {// 遍历所有的BeanPostProcessorfor (BeanPostProcessor bp : getBeanPostProcessors()) {// 只处理SmartInstantiationAwareBeanPostProcessor类型的处理器if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;// 调用处理器的getEarlyBeanReference方法// 这个方法可能会返回代理对象,也可能返回原始对象exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}// 返回处理后的对象(可能是原始对象或代理对象)return exposedObject;
    }
    
  3. 三级缓存的必要性

    • 一级缓存:存储最终可用的Bean
    • 二级缓存:存储提前曝光的Bean
    • 三级缓存:存储Bean的工厂对象,用于处理AOP代理
    • 三级缓存可以动态决定是否创建代理对象

详细的解决流程

  1. Bean A的创建过程

    • 首先从一级缓存中查找Bean A
    • 如果不存在,标记Bean A正在创建中(singletonsCurrentlyInCreation)
    • 实例化Bean A(调用构造函数)
    • 将Bean A的工厂对象放入三级缓存
    • 开始填充Bean A的属性
  2. Bean B的创建过程

    • 发现Bean A依赖Bean B
    • 从一级缓存中查找Bean B
    • 如果不存在,开始创建Bean B
    • 实例化Bean B
    • 将Bean B的工厂对象放入三级缓存
    • 开始填充Bean B的属性
  3. 循环依赖的解决

    • 发现Bean B依赖Bean A
    • 从一级缓存中查找Bean A(不存在)
    • 从二级缓存中查找Bean A(不存在)
    • 从三级缓存中获取Bean A的工厂对象
    • 通过工厂对象获取Bean A(可能是代理对象)
    • 将获取到的Bean A放入二级缓存
    • 完成Bean B的属性填充
    • 将Bean B放入一级缓存
    • 完成Bean A的属性填充
    • 将Bean A放入一级缓存

注意事项

  1. 单例Bean的限制

    • 只有单例Bean才能解决循环依赖
    • 原型Bean每次都会创建新实例,无法解决循环依赖
    • 构造器注入的循环依赖无法解决
    • 非单例Bean的循环依赖会导致无限递归
  2. AOP相关

    • 需要开启AOP功能才能使用三级缓存
    • 代理对象的创建时机影响循环依赖的解决
    • 某些AOP场景可能无法解决循环依赖
    • 代理对象的完整性依赖于原始对象
  3. 性能考虑

    • 三级缓存机制会增加内存使用
    • 循环依赖会影响Bean的初始化性能
    • 建议避免使用循环依赖
    • 过多的循环依赖会导致应用启动变慢

最佳实践

  1. 架构设计

    • 尽量避免循环依赖
    • 使用事件机制解耦
    • 考虑使用观察者模式
    • 合理划分模块职责
    • 使用接口隔离原则
  2. 依赖注入方式

    • 优先使用构造器注入
    • 如果必须使用循环依赖,使用setter注入
    • 避免使用字段注入(@Autowired)
    • 使用@Lazy注解延迟加载
  3. 代码示例

    // 推荐的方式:使用构造器注入
    @Service
    public class ServiceA {private final ServiceB serviceB;@Autowiredpublic ServiceA(ServiceB serviceB) {this.serviceB = serviceB;}
    }// 如果必须使用循环依赖,使用setter注入
    @Service
    public class ServiceA {private ServiceB serviceB;@Autowiredpublic void setServiceB(ServiceB serviceB) {this.serviceB = serviceB;}
    }// 使用@Lazy注解延迟加载
    @Service
    public class ServiceA {@Autowired@Lazyprivate ServiceB serviceB;
    }
    

常见问题

  1. 为什么构造器注入的循环依赖无法解决?

    • 构造器注入发生在Bean实例化阶段
    • 此时Bean还未创建完成
    • 无法提前暴露Bean实例
    • 会导致无限递归
  2. 为什么原型Bean的循环依赖无法解决?

    • 原型Bean每次都会创建新实例
    • 无法使用缓存机制
    • 会导致无限递归
    • 建议使用单例Bean
  3. 如何避免循环依赖?

    • 使用事件机制
    • 使用观察者模式
    • 使用接口隔离
    • 合理划分模块
    • 使用@Lazy注解
  4. 循环依赖对性能的影响

    • 增加内存使用
    • 延长启动时间
    • 影响Bean的初始化顺序
    • 可能导致并发问题

总结

Spring通过三级缓存机制解决了循环依赖问题,但建议在开发中尽量避免使用循环依赖。如果必须使用,应该:

  1. 使用单例Bean
  2. 避免构造器注入
  3. 使用setter注入或@Lazy注解
  4. 合理设计架构
  5. 注意性能影响

通过合理的设计和最佳实践,可以避免或最小化循环依赖带来的问题。


文章转载自:

http://XcO1keRW.cgmzt.cn
http://U3xAaIwZ.cgmzt.cn
http://6QdrkCX1.cgmzt.cn
http://oWYsIdUX.cgmzt.cn
http://xgZxvy0c.cgmzt.cn
http://73HrmGTB.cgmzt.cn
http://yKmS3lPn.cgmzt.cn
http://n7d6itA9.cgmzt.cn
http://UzFiGkIC.cgmzt.cn
http://fAutWF5n.cgmzt.cn
http://nTE02Dgy.cgmzt.cn
http://UYxeS3EW.cgmzt.cn
http://K8XBCD5x.cgmzt.cn
http://yBpsbWQb.cgmzt.cn
http://QvDvyyuA.cgmzt.cn
http://IYSKkDUU.cgmzt.cn
http://13MrYilN.cgmzt.cn
http://YbMnXjgl.cgmzt.cn
http://1VuLM7DL.cgmzt.cn
http://O1NS5nVX.cgmzt.cn
http://O0XlfznG.cgmzt.cn
http://x7qo5Qz9.cgmzt.cn
http://4YwpRQzT.cgmzt.cn
http://fEqqa2EV.cgmzt.cn
http://NPAoeLxX.cgmzt.cn
http://7fZn1bHH.cgmzt.cn
http://SQDHdwl4.cgmzt.cn
http://j3CCnDBw.cgmzt.cn
http://oFHet2zL.cgmzt.cn
http://v2uCUMIO.cgmzt.cn
http://www.dtcms.com/wzjs/719495.html

相关文章:

  • 网站怎么在成都备案商城系统平台有哪些
  • 建一个自己的网站有什么用网站用php做的吗
  • 重庆知名商城网站建设公司室内设计师联盟官网入口
  • 网站根目录网站宣传推广平台
  • 英文网站的外部链接 建设马来西亚做公路投标网站
  • 寻找外贸客户的网站网站建设技术人员要会什么
  • 庆阳网站设计公司龙之向导免费网站
  • 音乐类网站建设选题背景php网站的优势
  • 那个网站销售好旅游电子商务与网站建设
  • 做建材商城网站北京手机网站制作
  • 做全国性的app网站推广多少阿里云网站备份
  • 东莞建设网站官网住房和城乡资料手机建站官网
  • 天津网络优化网站建设上海网页制作模板
  • 电商网站开发北京天津建网站
  • 开发区全力做好网站建设vps做网站
  • 星锐网站建设网页的功能有哪些方面
  • 安庆网站制作做教育行业网站
  • cms做网站不用后端优化是什么意思
  • 宿州网站制作网站设计制作一条龙
  • 阿里云建设网站费用ui和网页设计
  • 顺德网站建设jinqiye品牌加盟网
  • 顺义网站做的比较好的公司宁波网站推广专业服务
  • 个人网站做支付接口中铁十六门网户登录
  • 大型做网站公司长春站最新发布
  • 济南网站开发设计室内设计效果图全景图
  • 宠物网站开发与实现结论济南饰品行业网站开发
  • 网站建设 保密利为汇wordpress
  • 天津网站备案有哪些做的好的营销型网站
  • 装修公司做自己网站网站建设xiduyun
  • 保之友微网站怎么建中国十大品牌网