阿亮随手录-SpringBoot启动流程、三级缓存要求、BeanFactory与FactoryBean、AutoWired与Resource、不推荐字段注入

记录一下学习的时候研究的一些小问题

SpringBoot 启动流程

  1. 执行 SpringApplication.run()

入口方法,启动整个程序。

  1. 创建 SpringApplication 对象

做一些初始化:

  • 判断是不是 Web 环境

  • 加载所有初始化器、监听器

  1. 启动监听、发布启动事件

发布 ApplicationStartingEvent,开始启动。

  1. 准备环境(Environment)

加载配置:application.yml、bootstrap.yml、系统变量、命令行参数等。

  1. 创建 Spring 容器(ApplicationContext)

根据环境创建:

  • Web 环境:创建 AnnotationConfigServletWebServerApplicationContext

  • 非 Web:创建 AnnotationConfigApplicationContext

  1. refresh 容器(核心)

执行 refresh() 方法:

  • 扫描 Bean

  • 加载自动配置类

  • 初始化所有单例 Bean

  • 完成 AOP、代理、依赖注入

  1. 启动 Web 服务器(Tomcat / Jetty)

如果是 Web 应用,在 refresh 过程中启动内置服务器。

  1. 发布启动完成事件,启动成功

执行 runners(ApplicationRunner、CommandLineRunner),启动完成。

也就是

  1. 调用 SpringApplication.run() 进入启动流程。

  2. 准备环境,加载配置文件。

  3. 创建并初始化 Spring 容器。

  4. 执行 refresh() 扫描、加载、初始化所有 Bean。

  5. 启动 Tomcat 等服务器,发布启动完成事件。

为什么三级缓存要求单例并且不能是构造函数注入

Spring 的循环依赖是通过三级缓存来解决的,

但是它有两个要求:不能使用构造函数注入,并且必须是单例 Bean。

首先说一下原理:

三级缓存的核心思路,就是提前暴露一个已经实例化、但是还没初始化的半成品 Bean,

用这个半成品去打破循环引用的链条。

那为什么不能用构造函数注入?

因为构造函数是在实例化阶段就需要依赖对象,

这个时候 Bean 连对象都还没创建出来,根本没有半成品可以暴露,

所以没办法打破循环,会直接报错。

然后为什么必须是单例?

因为单例 Bean 在整个容器里只有一个实例,

我们才能把这唯一的半成品提前暴露出去。

如果不是单例,每次获取都会创建新对象,

就没有一个固定的、唯一的半成品可以用来打破循环,

最终会无限创建对象,导致循环依赖无法解决。

BeanFactory 和 FactoryBean 的区别

BeanFactory 是 Spring 的顶层基础接口,它负责创建、管理 Bean 的整个生命周期。

我们可以通过 BeanFactory 来获取 Bean、查询 Bean 的信息和状态。

像我们常用的 ApplicationContext,它本身就是 BeanFactory 的一个扩展实现。

FactoryBean 是一个特殊的 Bean 接口。

如果一个类实现了 FactoryBean,Spring 在创建的时候,不会直接返回这个类本身的实例,

而是会去调用它里面的 getObject() 方法,用我们自己写的逻辑去创建一个复杂的对象,再交给 IOC 容器管理。

所以它主要用来自定义创建一些初始化比较复杂的 Bean。

@Autowired 与 @Resource 区别

  1. 来源不同
  • @Autowired 是 Spring 提供的注解,切换 IOC 容器需要改代码。

  • @Resource 是 JDK 标准注解,容器迁移不用改代码。

  1. 查找顺序不同
  • @Autowired :先按类型(byType)查找,类型找到多个,再按字段名匹配。

  • @Resource :先按名称(byName)查找,名称找不到,再按类型查找。

  1. 使用位置不同
  • @Autowired 可以用在:字段、set 方法、构造器。

  • @Resource 只能用在:字段、set 方法,不能用在构造器。

  1. 多 Bean 冲突处理
  • @Autowired 按类型找到多个 Bean,会自动按字段名匹配,匹配失败报错,可用 @Qualifier 指定 Bean 名称。

  • @Resource 先按名称匹配,名称找不到再按类型,类型找到多个也会报错,可直接用 name 属性或 @Qualifier 指定。

  1. 是否允许依赖不存在
  • @Autowired(required = false) :允许依赖不存在,值为 null。

  • @Resource 没有 required 属性,找不到 Bean 直接抛异常。

Spring 为什么不推荐使用字段注入,更推荐构造器注入

Spring 不推荐使用字段注入,更推荐构造器注入,原因主要有这几点:

  1. 更容易发现是否违反单一职责原则

字段注入时,依赖加得再多,代码看起来都很清爽,不容易察觉类太臃肿、职责太多。

换成构造器注入,参数一多就会显得很臃肿,能倒逼我们去拆分类,更符合设计原则。

  1. 单元测试更方便,不强依赖 Spring 容器

字段注入必须启动 Spring 容器才能赋值;

构造器注入可以直接 new 对象,手动传入 Mock 依赖,不用启动容器就能做单元测试。

  1. 可以用 final,保证不可变、更安全

字段注入不能用 final 修饰;

构造器注入可以声明为 private final ,依赖不可变、线程更安全。

  1. 避免初始化顺序导致的空指针 NPE

对象初始化顺序:

类加载 → 静态代码块 → 构造方法执行 → 才进行 @Autowired 字段注入。

如果在构造方法里使用字段注入的对象,此时依赖还没注入,直接空指针。

构造器注入在创建对象时就把依赖传进来,从根源避免 NPE。

  1. 循环依赖能更早暴露

字段注入的循环依赖,Spring 靠三级缓存可以勉强运行,运行时才可能出问题;

构造器注入的循环依赖,项目启动时直接报错,更早发现、更早解决。

  1. 封装性更好,不破坏封装

字段注入相当于直接操作类内部字段,破坏封装性;

构造器注入只通过构造方法传入依赖,类的内部状态对外不可见,更符合面向对象封装。

相关推荐
YDS8292 分钟前
SpringCloud —— Elasticsearch入门详解
spring·elasticsearch·spring cloud
毕设源码-赖学姐3 分钟前
【开题答辩全过程】以 滑雪场租赁管理系统的设计与实现为例,包含答辩的问题和答案
java
Javatutouhouduan11 分钟前
SpringBoot整合reids:JSON序列化文件夹操作实录
java·数据库·redis·html·springboot·java编程·java程序员
wen__xvn22 分钟前
模拟题刷题3
java·数据结构·算法
bug攻城狮24 分钟前
Spring Boot应用内存占用分析与优化
java·jvm·spring boot·后端
xiaodaidai丶27 分钟前
解决Sa-Token在 Spring MVC + WebFlux 混合架构中流式接口报错SaTokenContext 上下文尚未初始化的问题
spring·架构·mvc
無限進步D31 分钟前
Java 循环 高级(笔记)
java·笔记·入门
小六溜了41 分钟前
模块二十三.网络编程&正则表达式&设计模式
java
QWQ___qwq1 小时前
Spring Security + MyBatis-Plus 实现自定义数据库用户认证
数据库·spring·mybatis
de_wizard1 小时前
【mybatis】基本操作:详解Spring通过注解和XML的方式来操作mybatis
xml·spring·mybatis