阿亮随手录-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. 封装性更好,不破坏封装

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

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

相关推荐
独自破碎E1 小时前
BISHI61 小q的数列
java·开发语言
Dylan的码园2 小时前
从软件工程师看计算机是如何工作的
java·jvm·windows·java-ee
callJJ2 小时前
Spring AI Tool Calling(工具调用)详解——让大模型拥有“动手能力“
java·人工智能·spring·spring ai·tool calling
南部余额2 小时前
SpringBoot文件上传全攻略
java·spring boot·后端·文件上传·multipartfile
java1234_小锋2 小时前
Java高频面试题:什么是Redis哨兵机制?
java·redis·面试
苦学编程的谢3 小时前
好运buff机 ------ 测试报告
java·开发语言·功能测试
汤姆yu3 小时前
基于springboot的智能民宿预定与游玩系统
java·spring boot·后端
黎雁·泠崖3 小时前
Java常用类核心精讲 · 七篇精华总结
java·开发语言
逆境不可逃3 小时前
【从零入门23种设计模式01】创建型之工厂模式(简单工厂+工厂方法+抽象工厂)
java·spring·设计模式·简单工厂模式·工厂方法模式·抽象工厂模式·工厂模式