很难出错的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实例的时候,就得获取一个反射的代理实例,所以需要再来一个缓存,专门存工厂。

相关推荐
benchmark_cc几秒前
实时行情系统如何避免无意义的重复请求?从请求去重到缓存设计
大数据·开发语言·python·pandas·量化交易·股票数据·quantdash
田里的水稻1 分钟前
Python_两大法宝dir() 与 help() 的妙用
开发语言·python
郝学胜-神的一滴3 分钟前
Python浏览器自动化:Selenium与Playwright实战对比,网页爬虫&自动操作一站式指南
开发语言·爬虫·python·selenium·程序人生·自动化
金銀銅鐵6 分钟前
[Java] 用 GUI 展示 ArrayList 扩容的过程
java·后端·ai编程
土豆.exe8 分钟前
chunked 编码的行尾:Go、h11 与 Tomcat 各错在哪一处
开发语言·网络安全·golang·tomcat
磊 子9 分钟前
什么是虚假唤醒(spurious wakeup)?从 POSIX 标准讲到 futex 内核
java·开发语言
飘然生20 分钟前
traceId链路 skywalking java agent
java·开发语言·skywalking
li星野21 分钟前
【学习记录】射线补偿原理与实现:从物理模型到MATLAB代码
开发语言·学习·matlab
IT研究室26 分钟前
最新计算机毕业设计选题推荐-基于spring boot的母婴全程服务管理系统-网站-文档指导-Java-springboot
java·spring boot·课程设计
CRMEB34 分钟前
商品 SKU 设计,从简单规格到多规格踩过什么
java·前端·数据库·开源