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

湖北网站制作公司中国十大财务软件

湖北网站制作公司,中国十大财务软件,网站的作用和意义,title 芜湖网站制作1. Spring使用三级缓存来解决循环依赖问题 Spring使用三级缓存来解决循环依赖问题,‌而不是使用两级缓存。‌ 在Spring框架中,‌解决循环依赖的关键在于正确地管理Bean的生命周期和依赖关系。‌循环依赖指的是两个或多个Bean相互依赖,‌如果…

1.  Spring使用三级缓存来解决循环依赖问题

Spring使用三级缓存来解决循环依赖问题,‌而不是使用两级缓存。‌

在Spring框架中,‌解决循环依赖的关键在于正确地管理Bean的生命周期和依赖关系。‌循环依赖指的是两个或多个Bean相互依赖,‌如果处理不当,‌可能会导致Bean无法正确初始化。‌Spring通过引入三级缓存机制来解决这个问题,‌而不是使用两级缓存。‌

1.  一级缓存:‌存储已经初始化完成的Bean实例。‌当其他Bean依赖这个Bean时,‌可以直接从缓存中获取实例,‌避免重复创建。‌

2.  二级缓存:‌在某些情况下,‌二级缓存用于存储Bean的代理对象或原始实例,‌以避免重复创建。‌然而,‌在处理循环依赖时,‌二级缓存不足以解决问题,‌因为它不能确保在需要时总是提供正确的对象实例。‌

3.  三级缓存:‌这是为了解决二级缓存的局限性而引入的。‌三级缓存存储了一个函数接口及其实现,‌用于创建动态代理对象或调用BeanPostProcessor。‌通过这种方式,‌Spring可以确保在需要时提供正确的对象实例,‌无论是原始对象还是其代理对象,‌从而解决了循环依赖的问题。‌

通过这种方式,‌Spring能够确保在复杂的依赖关系中正确地管理和初始化Bean,‌避免了因循环依赖导致的初始化失败问题。‌这种三级缓存机制不仅提高了代码的可维护性,‌还优化了性能,‌确保了Spring应用程序的稳定性和可靠性

singletonObjects: 一级缓存,存储单例对象,Bean 已经实例化,初始化完成。 earlySingletonObjects: 二级缓存,存储 singletonObject,这个 Bean 实例化了,还没有初始化。 singletonFactories: 三级缓存,存储 singletonFactory。

2.  Bean 的创建过程

@Service
public class CircularServiceA {private String fieldA = "字段 A";
}

通过上面的流程,可以看出 Spring 在创建 Bean 的过程中重点是在 AbstractAutowireCapableBeanFactory 中的以下三个步骤:

实例化 createBeanInstance: 其中实例化 Bean 并对 Bean 进行赋值,像例子中的 fieldA 字段在这里就会赋值。

属性注入 populateBean: 可以理解为对 Bean 里面的属性进行赋值。(会依赖其他 Bean)

初始化 initializeBean: 执行初始化和 Bean 的后置处理器。

实例化赋值源码可以阅读:

BeanUtils.instantiateClass(constructorToUse)

3.  如果要依赖其他 Bean 呢?

那如果 CircularServiceA 依赖了其他 Bean 呢?

@Service
public class CircularServiceA {private String fieldA = "字段 A";@Autowiredprivate CircularServiceB circularServiceB;}
@Service
public class CircularServiceB {}

当 A 依赖了 B 的时候,在 createBeanInstance 这一步,并不会对 B 进行属性赋值。

而是在 populatedBean 这里查找依赖项,并创建 B。

4.  循环依赖下的创建过程

循环依赖的场景,在上一篇文章已经有所讲解,这里仅仅画图说明一下。

@Service
public class CircularServiceA {private String fieldA = "字段 A";@Autowiredprivate CircularServiceB circularServiceB;}
@Service
public class CircularServiceB {@Autowiredprivate CircularServiceA circularServiceA;
}

在 A 和 B 循环依赖的场景中:

B populatedBean 查找依赖项 A 的时候,从一级缓存中虽然未获取到 A,但是发现 A 在创建中。

此时,从三级缓存中获取 A 的 singletonFactory 调用工厂方法,创建 getEarlyBeanReference A 的早期引用并返回。

B 引用到 A ,B 就可以初始化完毕,然后 A 同样也可以初始化完毕了。

5.  二级缓存能否解决循环依赖

通过上面的图,仔细分析一下,其实把二级缓存拿掉,在 B 尝试获取 A 的时候直接返回 A 的实例,是不是也是可以的?

答案是:可以的!

但是为什么还是用三级缓存呢?

网上的很多资料说是和动态代理有关系,那就从动态代理的方面继续往下分析分析。

6.  动态代理的场景Bean 的创建过程

在 JavaConfig(配置类) 上添加 @EnableAspectJAutoProxy 注解,开启 AOP ,通过 Debug 循序渐进看一看动态代理对循环依赖的影响。

动态代理下,Bean 的创建过程

@Service
public class CircularServiceA {private String fieldA = "字段 A";public void methodA() {        System.out.println("方法 A 执行");}
}
@Aspect
@Component
public class AspectA {@Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")public void beforeA() {
        System.out.println("beforeA 执行");}
}

只有 A 的情况下,给 A 添加切面,开始 Debug。

前面的流程都相同,在 initializeBean 开始出现差异。

这一步需要初始化 Bean 并执行 Bean 的后置处理器。

其中有一个处理器为: AnnotationAwareAspectJAutoProxyCreator 其实就是加的注解切面,会跳转到 AbstractAutoProxyCreator 类的 postProcessAfterInitialization 方法

如图所示:wrapIfNecessary 方法会判断是否满足代理条件,是的话返回一个代理对象,否则返回当前 Bean。

后续调用 getProxy 、createAopProxy 等等,最终执行到下面一部分。

最终会执行到这里,AOP 代理相关的就不细看了。

一路放行,直到 initializeBean 执行结束。

此时发现:A 被替换为了代理对象。

所以 doCreateBean 返回,以及后面放到一级缓存中的都是代理对象。

7.有循环依赖的动态代理

这一次把循环依赖打开:

@Service
public class CircularServiceA {private String fieldA = "字段 A";@Autowiredprivate CircularServiceB circularServiceB;public void methodA() {        System.out.println("方法 A 执行");}
}
@Aspect
@Component
public class AspectA {@Before("execution(public void com.liuzhihang.circular.CircularServiceA.methodA())")public void beforeA() {        System.out.println("beforeA 执行");}}
@Service
public class CircularServiceB {@Autowiredprivate CircularServiceA circularServiceA;public void methodB() {}
}
@Aspect
@Component
public class AspectB {@Before("execution(public void com.liuzhihang.circular.CircularServiceB.methodB())")public void beforeB() {        System.out.println("beforeB 执行");}}

开始 Debug,前面的一些列流程,都和正常的没有什么区别。而唯一的区别在于,创建 B 的时候,需要从三级缓存获取 A。

此时在 getSingleton 方法中会调用:singletonObject = singletonFactory.getObject();

有时会比较疑惑 singletonFactory.getObject() 调用的是哪里?

所以这一块调用的是 getEarlyBeanReference,开始遍历执行 BeanPostProcessor。

看到 wrapIfNecessary 就明白了吧!这块会获取一个代理对象。

也就是说此时返回,并放到二级缓存的是一个 A 的代理对象。

这样 B 就创建完毕了!

到 A 开始初始化并执行后置处理器了!因为 A 也有代理,所以 A 也会执行到 postProcessAfterInitialization 这一部分!

但是在执行 wrapIfNecessary 之前,会先判断代理对象缓存是否有 A 了。

this.earlyProxyReferences.remove(cacheKey) != bean

但是这块获取到的是 A 的代理对象。肯定是 false 。 所以不会再生成一次 A 的代理对象。

8.总结

可以看到,循环依赖下,有没有代理情况下的区别就在:

singletonObject = singletonFactory.getObject();

在循环依赖发生的情况下 B 中的 A 赋值时:

无代理:getObject 直接返回原来的 Bean

有代理:getObject 返回的是代理对象

然后都放到二级缓存。

9.为什么要三级缓存?

假设去掉三级缓存

去掉三级缓存之后,Bean 直接创建 earlySingletonObjects, 看着好像也可以。

如果有代理的时候,在 earlySingletonObjects 直接放代理对象就行了。

但是会导致一个问题:在实例化阶段就得执行后置处理器,判断有 AnnotationAwareAspectJAutoProxyCreator 并创建代理对象。

这么一想,是不是会对 Bean 的生命周期有影响。

同样,先创建 singletonFactory 的好处就是:在真正需要实例化的时候,再使用 singletonFactory.getObject() 获取 Bean 或者 Bean 的代理。相当于是延迟实例化。

假设去掉二级缓存

如果去掉了二级缓存,则需要直接在 singletonFactory.getObject() 阶段初始化完毕,并放到一级缓存中。

那有这么一种场景,B 和 C 都依赖了 A。

要知道在有代理的情况下 singletonFactory.getObject() 获取的是代理对象。

而多次调用 singletonFactory.getObject() 返回的代理对象是不同的,就会导致 B 和 C 依赖了不同的 A。

那如果获取 B 到之后直接放到一级缓存,然后 C 再获取呢?

一级缓存放的是已经初始化完毕的 Bean,要知道 A 依赖了 B 和 C ,A 这时候还没有初始化完毕。

http://www.yayakq.cn/news/402773/

相关文章:

  • 素材网站定制win7怎么做网站域名绑定
  • 微页制作网站模板PHP 网站开发 重点知识
  • 建p2p网站网站备案要交钱吗
  • 手表网站 云深圳西丽网站建设公司
  • 和嗲囡囡和做的网站北京网页设计公司兴田德润团队
  • 个人站长网站应该如何定位做720效果的还有哪个网站
  • 广州大型网站建设网站平台运营方案
  • 替老外做网站义乌网站搭建
  • 广元网站建设价格学广告设计要学多久
  • 网站建设过程总结报告物联网服务平台
  • 网站开发团队投入招商网站建设定做
  • 网站在建设中页面企业所得税怎么合理节税
  • 怎样做免费企业网站高端网站建设加盟
  • 济南网站建设分销商城wordpress 加载慢
  • 长沙市做网站的网站织梦dedecms教育培训网站模板
  • 天津制作公司网站数据库修改wordpress登录密码忘记
  • 织梦cms怎样做网站阳江网站制作公司
  • 公司建网站怎么弄网页传奇服务端
  • 如何建设教育信息网站察隅网站建设
  • 网站开发项目实训总结泉州网站建设价格
  • 自己做电商网站.页面设计原型图
  • 网站建设了流程php7跨设备网站开发pdf
  • 河北网站建设品牌大全怎么才可以做网站
  • art2008cms网站开发做外单阿里的网站
  • 免费自己制作网站源代码代做网站
  • 广阳区建设局网站网站代运营收费
  • 网站开发人才储备近期国内热点新闻事件
  • 做得不好的知名企业网站建筑木工模板承包报价单
  • 个人建站流程详解本地开发app的公司在哪里
  • 英文网站建设目的网站开发前端好还是后端好