spring的几个八股

bean生命周期

其实可以分成三个阶段来记:创建 → 初始化 → 销毁,初始化阶段是重点。

创建:反射调构造方法实例化,然后注入属性和依赖。

初始化:按顺序经历四步------

  1. Aware 回调(让 Bean 感知到容器,比如拿到 ApplicationContext
  2. BeanPostProcessor 前置处理
  3. 执行初始化方法(@PostConstructafterPropertiesSet()init-method,三选其一或叠加)
  4. BeanPostProcessor 后置处理 (AOP 代理在这里生成,返回代理对象替换原始 Bean)

销毁 :容器关闭时,执行销毁方法(@PreDestroydestroy()destroy-method)。

springboot启动

Spring Boot 启动的入口是 SpringApplication.run(),里面核心干了这几件事:

第一步,创建 SpringApplication 对象 :判断应用类型(是不是 Web 应用),加载 spring.factories 里的初始化器和监听器。

第二步,准备环境 :加载 application.yml / application.properties,处理命令行参数、环境变量,确定激活的 profile。

第三步,创建 Spring 容器ApplicationContext):根据应用类型创建对应的容器,Web 应用创建 AnnotationConfigServletWebServerApplicationContext

第四步,刷新容器refresh()):这是最核心的一步------

  • 执行包扫描,注册你写的 Bean
  • 执行自动配置,把符合条件的自动配置类里的 Bean 也注册进来
  • 实例化所有单例 Bean,走完生命周期
  • 启动内嵌 Tomcat

第五步,启动完成 :发布 ApplicationReadyEventApplicationRunner / CommandLineRunner 执行,服务就绪。

Spring 容器工作过程

复制代码
扫描 @Component 等注解
       ↓
注册 BeanDefinition(只登记类信息,不创建对象)
       ↓
实例化 Bean(反射调构造方法 new 出来)
       ↓
依赖注入(解析 @Autowired,从容器找对应 Bean,反射赋值)
       ↓
Aware 回调(让 Bean 感知容器,拿到 ApplicationContext 等)
       ↓
BeanPostProcessor 前置处理
       ↓
执行初始化方法(@PostConstruct → afterPropertiesSet → init-method)
       ↓
BeanPostProcessor 后置处理(AOP 代理在这里生成)
       ↓
放入容器,对外提供服务

第四步 refresh()

实例化所有单例 Bean

这里就是 Bean 的完整生命周期:

实例化(反射调构造方法)

属性注入(@Autowired)

Aware 回调(获取 Bean 名称、BeanFactory、环境信息等)

BeanPostProcessor 前置处理(修改属性、检查配置)

初始化方法(@PostConstruct)

BeanPostProcessor 后置处理(生成代理)

放入容器使用

容器关闭时销毁(@PreDestroy)

自动配置

@EnableAutoConfiguration 会读取 META-INF/spring.factories 中的自动配置类。底层通过 ImportSelector 动态导入这些配置类,ImportSelector 会根据条件决定哪些配置类需要生效。

每个自动配置类通常配合条件注解,如 @ConditionalOnClass@ConditionalOnMissingBean@ConditionalOnProperty,只有满足条件的 Bean 才会注册到容器。

用户如果自己定义了同类型 Bean,会覆盖默认 Bean,从而实现可定制化。

注: 读取META-INF/spring.factories(Spring Boot 2.x)或 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@SpringBootApplication:是三个注解的组合:

  • @SpringBootConfiguration:标识这是配置类
  • @EnableAutoConfiguration:开启自动配置,读取 spring.factories 里的自动配置类按条件装载
  • @ComponentScan:扫描当前包及子包下所有 @Component@Service@Repository@Controller 注解的类注册成 Bean

Spring 三级缓存解决循环依赖

先说为什么需要三级缓存,而不是直接说三个缓存是什么。

A 依赖 B,B 依赖 A,如果没有缓存机制,创建 A 时发现需要 B,去创建 B 时又发现需要 A,死锁了。解决思路是:A 实例化之后还没初始化完,先把自己暴露出去,让 B 能拿到 A 的引用,B 完成初始化后 A 再继续完成自己的初始化。


三个缓存分别存什么

  • 一级缓存:完整的 Bean,初始化完成可以直接用
  • 二级缓存:半成品 Bean,实例化了但还没初始化完
  • 三级缓存:Bean 的 ObjectFactory,用来按需生成早期引用,如果需要 AOP 代理在这里生成

完整过程

复制代码
创建 A,实例化后把 A 的 ObjectFactory 放入三级缓存
       ↓
A 注入属性,发现需要 B,去创建 B
       ↓
创建 B,实例化后把 B 的 ObjectFactory 放入三级缓存
       ↓
B 注入属性,发现需要 A,去三级缓存找到 A 的工厂
调用工厂拿到 A 的早期引用(需要代理就生成代理),放入二级缓存,删除三级缓存里的 A
       ↓
B 拿到 A 的早期引用,完成初始化,放入一级缓存
       ↓
回到 A,拿到完整的 B,A 完成初始化,放入一级缓存,删除二级缓存里的 A

为什么必须三级,二级不够

实例化 → 属性注入 → 初始化 → BeanPostProcessor后置处理生成代理 → 放入容器

二级放 ObjectFactory:多个 Bean 依赖 A 时,每次调用工厂生成一个新代理,B 和 C 拿到的不是同一个对象,不一致。

A 实例化 → A 的 ObjectFactory 放入二级缓存

A 注入属性,发现需要 B

创建 B,B 注入属性发现需要 A

B 调用二级缓存的 ObjectFactory 生成代理A1,注入给 B

创建 C,C 注入属性发现需要 A

C 调用二级缓存的 ObjectFactory 生成代理A2,注入给 C

B 里持有代理A1,C 里持有代理A2,A1 和 A2 不是同一个对象 ← 问题

二级放原始对象:B 拿到原始 A,最终容器里放的是代理 A,B 持有的引用和容器里的不是同一个对象,不一致。

A 实例化 → 原始A放入二级缓存

A 注入属性,发现需要 B

创建 B,B 注入属性发现需要 A

B 从二级缓存拿到原始A,注入进去,B 完成初始化

回到 A,继续完成初始化

BeanPostProcessor 后置处理 → 生成代理A

代理A放入一级缓存

构造器注入无法解决:三级缓存的前提是先实例化再注入,构造器注入是实例化时就要注入,A 还没 new 出来就需要 B,放不进三级缓存,直接报错。

相关推荐
逸Y 仙X2 小时前
文章九:ElasticSearch索引字段常见属性
java·大数据·服务器·数据库·elasticsearch·搜索引擎
左左右右左右摇晃2 小时前
Java笔记——多态
java·笔记·python
空空潍2 小时前
2026年IDEA、PyCharm等专业版学生免费申请教育许可证
java·ide·intellij-idea
24白菜头2 小时前
若依框架Ruoyi-Vue-SpringBoot3部署
前端·javascript·笔记·后端·学习
MX_93592 小时前
基于注解方式配置声明式事务
java·开发语言·后端·spring
Java面试题总结2 小时前
Spring AI 初步集成(2)-添加记忆
java·人工智能·spring
野犬寒鸦2 小时前
JVM垃圾回收机制深度解析(G1篇)(垃圾回收过程及专业名词详解)
java·服务器·jvm·后端·面试
清水白石0082 小时前
协程不是线程:深入理解 Python async/await 运行机制
java·linux·python
程序员老乔2 小时前
Java 新纪元 — JDK 25 + Spring Boot 4 全栈实战(五):FFM API,告别JNI在Spring Boot中直连推荐引擎
java·开发语言·spring boot