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

上海公司做网站的价格天天seo站长工具

上海公司做网站的价格,天天seo站长工具,软件库合集资料网站,做网站 传视频 用什么笔记本好文章目录 关键代码相关知识为什么要使用二级缓存为什么要使用三级缓存只使用两个缓存的问题不能解决构造器循环依赖为什么多例bean不能解决循环依赖问题初始化后代理对象赋值给原始对象解决循环依赖SpringBoot开启循环依赖 循环依赖 在线流程图 关键代码 从缓存中查询getSingl…

文章目录

    • 关键代码
    • 相关知识
      • 为什么要使用二级缓存
      • 为什么要使用三级缓存
      • 只使用两个缓存的问题
      • 不能解决构造器循环依赖
      • 为什么多例bean不能解决循环依赖问题
      • 初始化后代理对象赋值给原始对象
      • 解决循环依赖
      • SpringBoot开启循环依赖



循环依赖 在线流程图

在这里插入图片描述



关键代码

从缓存中查询getSingleton()方法

protected Object getSingleton(String beanName, boolean allowEarlyReference) {// Quick check for existing instance without full singleton lock// 一级缓存 二级缓存中取Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// Consistent creation of early reference within full singleton lock// 加锁后再查询 重新取一遍singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;
}

经过单例Bean判断和是否允许开启依赖注入判断后,往第三级缓存中存ObjectFactory函数式接口对象

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 循环依赖-添加到三级缓存addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

初始化后将循环依赖产生的代理对象赋值给普通对象

	if (earlySingletonExposure) {// 从二级缓存中取代理对象Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {// 赋值给当前普通对象if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {// beanName被哪些bean依赖了,现在发现beanName所对应的bean对象发生了改变,那么则会报错String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] 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 " +"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");}}}}



相关知识

案例:A对象 与 B对象互相引用,A对象 与 C对象互相引用。



为什么要使用二级缓存

作用:

  • 二级缓存中存储的是不完整的早期的bean。
  • 二级缓存使不完整的bean和完整的bean分开存储,保证并发线程安全,提高性能

假如不使用二级缓存,只有一级缓存的情况。一级缓存它想要打破循环的话就只能把半成品Bean存放在一级缓存中,这样创建B对象时就获取到一级缓存中的半成品A对象了,这样也就打破了对象互相引用的死循环。

问题是多线程访问时,其他线程可能就拿到了A对象取使用了。

解决方法是对整个getBean()方法加锁,这样的问题是锁的粒度太大了,而且想获取已经创建好了的对象也需要等待

所以就加入了二级缓存,用二级缓存来降低锁的粒度,提升性能,



为什么要使用三级缓存

作用:

  • 解决循环依赖的死循环
  • 使用ObjectFactory函数式接口,提升bean创建过程扩展性,保证规范,代码职责单一性

如果不使用三级缓存,同时A对象要进行AOP的话,此时就会出现依赖注入给B对象中的属性的A的普通对象,A经过创建过程之后存入单例池的却是代理对象

如果想要解决上面的问题就需要在创建bean对象的过程中,在实例化之后,属性填充之前就进行AOP操作生成代理对象,并存入二级缓存中。

这种方式就破坏了Bean创建过程的规范,同时还会出现循环依赖多次创建多次动态代理对象。

所以解决方法是我只判断出现了循环依赖这种情况才对该bean提前进行AOP操作。这些操作代码我不能加在创建bean的各个小步骤中,为了保证代码职责单一性。所以便有了三级缓存来保存ObjectFactory函数式接口对象,在出现了循环依赖的情况下就去执行对应方法。



只使用两个缓存的问题

如果只使用一级和二级缓存,那么就需要考虑AOP问题,可能给对象B注入的是普通对象,存入单例池是的代理对象

如果只使用一级和三级缓存,A对象与B对象互相引用,A对象与C对象互相引用。在执行行A对象就会创建两个AOP代理对象分别注入给B和C



不能解决构造器循环依赖

因为在创建Bean的总流程中,先进行实例化,完成之后才会去往三级存储中存入数据。此时实例化都没有完成,普通对象都没有创建成功,所以无法处理

解决方法是使用@Lazy注解



为什么多例bean不能解决循环依赖问题

我们从源码中是可以发现,再往三级缓存中存数据前是进行了if判断的,只有单例bean才会加入到三级缓存中。

其实解决循环依赖核心就是使用了一个Map,而这个map就相当于一个缓存。

我们bean是单例的,而且注入是通过setter字段注入的,单例意味着只需要创建一次对象,后续就可以从缓存中取出来,字段注入意味着我们无需调用构造方法进行注入。

  • 如果是原型Bean,那么意味着每次都要去创建bean,无法利用缓存
  • 如果是构造方法注入,那么意味着需要调用构造方法注入,也无法利用缓存



初始化后代理对象赋值给原始对象

在bean的创建过程中,还有一个地方跟循环依赖有关。

如果A是动态代理对象,通过三级缓存循环依赖之后,就会出现B.a = A的代理对象。但进行了初始化整个过程的A对象是普通对象,所以此时就需要把二级缓存中的代理对象赋值给BeanA。

	if (earlySingletonExposure) {// 1. 从二级缓存中取出代理对象Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {// 2. 赋值给普通对象// 这里会判断初始化过程中是否改变了当前对象,如果改变了则下面的if判断不成立if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {// beanName被哪些bean依赖了,现在发现beanName所对应的bean对象发生了改变,那么则会报错String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] 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 " +"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");}}}}
  • @Async注解会在初始化过程中创建动态代理对象,导致上面if判断中得到的代理对象和原始bean对象不一致。故而抛出异常
  • @Transactional注解也是代理对象,为什么不会报错?这是因为@Transaction注解的代理对象和AOP实际上是一个代理对象。

在解决循环依赖时,我就通过了普通对象创建了一个代理对象,解决你在初始化过程中有改变了普通对象,那我之前创建的代理对象岂不是没用了。



解决循环依赖

如果关闭了循环依赖功能,如果有两种方式解决循环依赖

  1. 使用@Lazy注解加在属性上,代表Spring容器加载时注入,会临时注入一个代理对象,等真正使用的时候再通过代理对象去调用最终的getBean()方法
  2. 添加一个中间类, 中间类去依赖A\B, 然后让中间类去组织他们的依赖方法

在这里插入图片描述



SpringBoot开启循环依赖

不建议开启,能产生循环依赖问题的代码本身就是一种不规范的设计。

Spring作者都已表示可能会在后续版本去掉循环依赖支持。 除非你是把 Spring代码移植到SpringBoot ,可以考虑开启循环依赖已保证之前代码正常性。

spring.main.allow-circular-references=true
http://www.hkea.cn/news/714952/

相关文章:

  • 惠州悦商做网站软件开发一般需要多少钱
  • 做衣服外单网站优化大师官方正版下载
  • 专门做酒店的网站百度排行
  • 上海做手机网站建设盐城网站优化
  • html论坛模板东营seo整站优化
  • 天津网站建设582345网址导航桌面版
  • 东莞纸箱厂东莞网站建设经典模板网站建设
  • 贺州同城购物网站建设中国网站排名100
  • 黄骅港旅游景点爱站网seo工具包
  • 网站 图文混编提高网站搜索排名
  • 北京怀柔网站制作教育机构
  • 网站建设费 大创友链交换平台
  • o2o商城网站系统开发微信群拉人的营销方法
  • 帝国cms做淘宝客网站网页设计用什么软件
  • 营销型网站建设的优缺点视频优化软件
  • 珠海响应式网站建设推广公司网络营销发展方案策划书
  • 中国人自己的空间站每日英语新闻
  • 教师可以做网站吗seo常用工具包括
  • 武山建设局网站什么是seo
  • 做文案需要用到的网站全网模板建站系统
  • 苏州乡村旅游网站建设策划书网站建设百度推广
  • 12380网站建设情况总结百度浏览器入口
  • 直播网站开发要多久排行榜前十名
  • 网站备案完才能建站吗企业建站公司
  • 网站开发外包合同西安网站优化公司
  • 2022网页设计尺寸规范和要求怎么做seo关键词优化
  • 北京大学两学一做网站十大收益最好的自媒体平台
  • 网站开发服务费企业网站建设的一般要素
  • 台州企业网站制作公司郴州网站推广
  • 如何做移动端网站邮件营销