首页 > 其他分享 >四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!

时间:2022-11-03 12:36:21浏览次数:82  
标签:BeanPostProcessor 依赖 Spring ServiceAImpl 缓存 循环 ServiceBImpl

开心一刻

  那天知道她结婚了,我整整一个晚上没睡觉,开了三百公里的车来到她家楼下,缓缓的抽了一支烟......

  天渐渐凉了,响起了鞭炮声,迎亲车队到了,那天披着婚纱的她很美,真的很美!

  我跟着迎亲车队开了几公里的时候,收到了她的信息:别送了,别送了,你的手扶拖拉机太响了 ......

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_spring

前情回顾

  楼主一而再,再而三的折腾循环依赖,你们不烦,楼主自己都烦了,如果你们实在是受不了,那就...

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_三级缓存_02

  言归正传,虽然确实有点像懒婆娘的裹脚布,又臭又长,但确实还是有点东西的,只要大家坚持看完,肯定会有收获的!

  我们先回顾下前三探

  一探

  ​​Spring 的循环依赖,源码详细分析 → 真的非要三级缓存吗​​ 中讲到了循环依赖问题

Spring 通过三级缓存解决 setter

singletonObjects

earlySingletonObjects

singletonFactories

  着重分析了是否一定需要三级缓存来解决循环依赖问题

  二探

Spring

  ​​再探循环依赖 → Spring 是如何判定原型循环依赖和构造方法循环依赖的​​,从源码的角度分析了 Spring

Set<String> singletonsCurrentlyInCreation 会记录当前正在创建中的实例名称, Spring 创建实例对象之前,会判断 singletonsCurrentlyInCreation

ThreadLocal<Object> prototypesCurrentlyInCreation 会记录当前线程正在创建中的原型实例名称, Spring 创建原型实例对象之前,会判断 prototypesCurrentlyInCreation

  三探

  ​​三探循环依赖 → 记一次线上偶现的循环依赖问题​​,从源码的角度分析了这次偶现问题可能出现的原因

BeanDefinition

BeanDefinition 覆盖, @Configuration + @Bean 修饰的 BeanDefinition 会覆盖 @Component 修饰的 BeanDefinition , BeanDefinition 的覆盖并不影响 BeanDefinition

Bean

  一通分析下来,虽说没能找到问题的真正原因,但至少知道了如何去规避这个问题,如何正确的书写规范的代码

问题复现

Spring

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_spring_03

BeanPostProcessor

SpringBoot 版本 2.0.3.RELEASE ,示例代码地址:​​spring-circular-beanpostprocessor​

  我们只需要关注三个类

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_spring_04

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_Spring 循环依赖_05

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_Spring 循环依赖_06

ServiceAImpl 依赖 ServiceBImpl , ServiceBImpl 也依赖 ServiceAImpl

BeanPostProcessor

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_三级缓存_07

  她俩的爱情信息:

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'serviceAImpl': Bean with name 'serviceAImpl' has been injected into other beans [serviceBImpl] 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 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.

  此刻,楼主才明白,小丑竟是我自己!

问题分析

serviceAImpl 作为循环依赖的一部分注入到了 serviceBImpl 后,又被包装了,这就意味着 serviceBImpl 引用的不是最终版本的 serviceAImpl

BeanPostProcessor ,楼主不想过多介绍,大家可以查看:​​Spring拓展接口之BeanPostProcessor,我们来看看它的底层实现​​

Spring

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_spring_08

ServiceAImpl 比 ServiceBImpl 先被扫描,所以 serviceAImpl

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_Spring 循环依赖_09

serviceAImpl 填充属性serviceBImpl 完成之后,我们来 debug

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_Spring 循环依赖_10

debug 结果可以看到, ServiceBImpl 的实例对象 ServiceBImpl@5171 中注入的 ServiceAImpl 对象是 ServiceAImpl@5017

initializeBean(beanName, exposedObject, mbd); 后, Spring 暴露出来的 ServiceAImpl 的最终对象是 $Proxy53@5212

ServiceBImpl@5171 中注入的 ServiceAImpl@5017 并不是最终版本的 ServiceAImpl

问题处理

  面对这样的问题,我们可以怎么处理了

  @Lazy

@Lazy

@Lazy

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_三级缓存_11

 

  或者

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_spring_12

@Lazy

  SmartInstantiationAwareBeanPostProcessor

BeanPostProcessor ,改用 SmartInstantiationAwareBeanPostProcessor

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_Spring 循环依赖_13

getEarlyBeanReference ,而非 postProcessAfterInitialization

四探循环依赖 → 当循环依赖遇上 BeanPostProcessor,爱情可能就产生了!_spring_14

ServiceAImpl

ServiceBImpl 对象填充 serviceAImpl 属性时,就用第三级缓存中的 ServiceAImpl

  剔除循环依赖

  循环依赖本就不合理,项目中应尽量避免

  至于如何剔除,无法一概而论,需要大家自己去琢磨了

总结

  循环依赖

Spring 通过三级缓存解决了 setter

  循环依赖本就不合理,尽量去规避

  真实项目问题

循环依赖 遇上 BeanPostProcessor

Shareding-JDBC 做分库,而 Shareding-JDBC 又通过 BeanPostProcessor

  就这样,她俩就相遇了



标签:BeanPostProcessor,依赖,Spring,ServiceAImpl,缓存,循环,ServiceBImpl
From: https://blog.51cto.com/u_13423706/5819384

相关文章

  • 还在用双层for循环吗?太慢了
    前情提要我们在开发中经常碰到这样的场景,查出两个list集合数据,需要根据他们相同的某个属性为连接点,进行聚合。但是平时我们使用的时候关注过性能吗?下面让我们一起来看看......
  • 学习vue3(五)(插槽slot,Teleport传送组件,keep-alive缓存组件,依赖注入Provide / Inject)
    插槽slot插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot>表示,父组件可以在这个占位符中填充任何模板代码,如HTML、组件等,填充的内容会替换子组件的<slot......
  • 控制循环
    目录控制循环选择循环的种类循环的灵活度和检查位置什么时候使用while循环什么时候用带退出的循环循环控制前言进入循环控制循环使用循环是编程中最复杂的方面之一:知道......
  • vue导入项目缺少依赖‘node_modules’
    从git下载好的项目,导入vue时提示‘node_modules’依赖则需要在你的项目包下面找是否有package-lock.json文件,如:如果有,但是依旧报错,直接删除package-lock.json文件,用终......
  • 循环~倒数字三角形
    题目描述数字三角形的特征是三角形由数字填满,每行数字从1到9再到0周而复始挨个排列。你的任务是根据整数值输出倒数字三角形。输入输入数据含有一些整数,其值在[1,40]之......
  • 循环~画高楼
    题目描述小明想拥有一栋有烟囱的房子。 请写程序从键盘输入n(限定n为小于10的正整数),然后参照样例,用"*"画出一栋n层的楼房。 不管有几层,楼房的屋顶和烟囱都是一样的。 ......
  • 依赖注入_非空断言,css使用v-bind
    provide依赖注入//provide和inject通常成对一起使用,使一个祖先组件作为其后代组件的依赖注入方,无论这个组件的层级有多深都可以注入成功,只要他们处于同一条组件链上。......
  • 求和及求平均-while循环语句的应用-2022-11-2
    packagescanner;importjava.util.Scanner;publicclassDemo04{publicstaticvoidmain(String[]args){Scannerscanner=newScanner(System.in);......
  • Linux 循环,格式以及使用方法
    ​for循环:(每读取一行在字符串下面添加#号)[root@localhost]#cat/etc/passwd|awk-F':''{print$3}'|tail-5>test1.txt[root@localhost]#cattest1.txt98972......
  • Financial - 多因子shock, 路径依赖
    一、结论现有情景分析相关模型里面:实际pnl(actualPnL)都是非路径依赖的但一旦drilldown,分步骤的pnl还是路径依赖的,表现为不同的pathshock出来的中间结果不同,但是最后的a......