很难出错的Spring5(十)—— IOC进阶

一、Bean的生命周期

  • 创建:就是执行new,构造方法
  • 依赖注入/属性注入
    • 指的就是@Autowired注解的这些变量
    • 从三级缓存里拿
  • 初始化
    • 算出来一些以后肯定会用到的东西
    • 用@PostConstruct注解生命的方法在这个时候执行
  • 销毁。相当于把引用赋值为null,就可以被回收了

二、单例Bean的线程安全问题

spring默认bean是单例的,你可以认为"资源只有一份",比如某个Service类加了@Service注解,那么这个service的实例只有一份,当很多个请求进来的时候,都一起用这个实例,就要考虑bean的线程安全问题。有这么几种防御式编程的手段

  • synchronized关键字
  • ThreadLocal
  • 除了static类变量、final变量、以及@Autowired注入的变量之外。其余变量不许出现在成员变量里,只能出现在每个方法的内部。
  • 修改作用域,把单例改成prototype

三、如何解决循环依赖问题?

循环依赖:A类里用@Autowired注入了B类的实例,B类用@Autowired注入了A类的实例

解决方式:借用spring的三级缓存,底层逻辑是"提前暴露半成品"。

  • 一级:放bean成品
  • 二级:放未注入属性的半成品
  • 三级:工厂库。主要解决AOP带来的问题

创建完A的bean,A先在三级缓存里放一个自己的A工厂实例,准备依赖注入B的实例的时候,于是去Spring一级缓存一直找到三级缓存找B,发现B还没有被创建出来,所以去实例化B。B实例经过创建阶段,(B也在三级缓存放一个自己的工厂实例,但是因为本次叙述是A打头的,所以没用到),走入依赖注入阶段,开始从缓存里找A的实例,从一级缓存开始找一直找到三级缓存,发现可以在三级缓存中通过对象工厂拿到A,虽然A不太完善,但是已经存在,就把A放入二级缓存,同时删除三级缓存中的A,此时B已经完成注入了,成为一个成品,于是把B放入一级缓存。接着对A进行注入,现在A可以从一级缓存拿到B了,此时A也完成了注入成为了一个成品,于是删除二级缓存里的A,把A放入一级缓存。

总感觉跟"分布式事务"、"两阶段提交"、"TCP三次握手"这些思想的底层逻辑有点像,因为都有"半成品"出现。感觉第三级缓存就像是用来做标记用的。跟解决死锁的思路也很像

3.1 为什么需要三级缓存?

因为A这个类可能被@Transactional注解修饰或者自定义切面 @Aspect 能匹配到 A 类中的方法,也就是A生成的实例bean并不是原生的,而是一个代理实例。如果只有一二级缓存的话,B拿到的是原生实例,而A最后生成并放到一级缓存里的是代理实例,二者不一致了。

所以B去获取A实例的时候,就得获取一个反射的代理实例,所以需要再来一个缓存,专门存工厂。

相关推荐
小羊没烦恼!4 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里4 天前
java中的继承和多态的区别
java
小羊没烦恼!4 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕4 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码4 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖4 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时4 天前
01-06-A-JVM排查实战详解
java·jvm
伞伞悦读4 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
vipxieliang4 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot
C语言小火车4 天前
C/C++ 为什么需要编译器?
开发语言·c++