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

相关推荐
减瓦1 小时前
Gradle 版本演进全景:构建工具的进化密码
java·gradle
Ulyanov1 小时前
雷达导引头仿真中的坐标系——从惯性系到量测角
开发语言·python·算法·系统仿真·雷达信号处理·雷达导引头
减瓦1 小时前
Jackson 使用指南
java·spring boot·json
Devin~Y1 小时前
互联网大厂 Java 面试实录:Spring Boot、MyBatis、Redis、Kafka、Spring Security、RAG 与 MCP 全链路问答
java·redis·kafka·mybatis·spring security·spring mvc·sprint boot
weixin_437662981 小时前
redisson
java
码农进化录1 小时前
Java 程序员的 AI 进化论 | AI 加 Postman 跑接口测试,省了三天活
java·后端·openai
前鼻音太阳熊2 小时前
【MES系统】- 工业HMI状态机可视化实践:从手写SVG到Cytoscape.js的技术选型与踩坑
开发语言·javascript·ecmascript
阿米亚波2 小时前
【C++ STL】std::array
java·开发语言·c++
花生了什么事o2 小时前
JVM运行时数据区详解:变量、对象和类信息到底存在哪里
jvm