【java面试题】springboot的生命周期

下面我按真实工程视角把 Spring Boot 的生命周期完整梳理一遍,不讲空概念,直接对应你在写后端、调试、排错时到底发生了什么。

一句话总览(先给你一个"时间轴感")

JVM 启动 → SpringApplication 启动 → 创建 ApplicationContext → 扫描 Bean → Bean 实例化 & 注入 → 容器刷新完成 → Web 服务器启动 → 应用可用 → 运行中 → 关闭容器

一、应用启动阶段(main 方法开始)

public static void main(String[] args) {

SpringApplication.run(Application.class, args);

}

这一行背后干了什么?

  1. 创建 SpringApplication 对象

  2. 推断应用类型

• Servlet(最常见)

• Reactive

• None

  1. 加载:

• ApplicationContextInitializer

• ApplicationListener

  1. 设置环境 Environment(profiles、配置文件)

⚠️ 这一步还没创建任何 Bean

二、准备阶段(Environment & Context 准备)

1️⃣ 创建并准备 Environment

加载配置源(优先级很关键):

  1. 命令行参数

  2. application.yml / properties

  3. 系统环境变量

  4. JVM 参数

👉 你在 @Value、@ConfigurationProperties 能拿到值,依赖的就是这一步

2️⃣ 创建 ApplicationContext

根据 Web 类型:

• AnnotationConfigServletWebServerApplicationContext

• AnnotationConfigApplicationContext

⚠️ 此时容器已创建,但还没装 Bean

三、核心阶段:容器刷新(最关键)

真正的 Spring 生命周期从这里开始

核心方法:

AbstractApplicationContext#refresh()

① Bean 定义加载(扫描)

• 扫描:

• @Component

• @Service

• @Repository

• @Controller

• @Configuration

• 解析:

• @Bean

• @Import

• @Conditional

📌 这一步只是 注册 BeanDefinition

📌 没有 new 对象

② BeanFactoryPostProcessor 阶段

发生在 实例化之前

典型代表:

• ConfigurationClassPostProcessor

• PropertySourcesPlaceholderConfigurer

👉 能改 Bean 定义

👉 不能拿到 Bean 实例

③ Bean 实例化 & 依赖注入(重点)

对每个 Bean(默认单例):

生命周期顺序(非常重要)

  1. 构造方法

  2. @Autowired / 构造器注入

  3. Aware 接口回调

• BeanNameAware

• ApplicationContextAware

  1. BeanPostProcessor#postProcessBeforeInitialization

  2. @PostConstruct

  3. InitializingBean.afterPropertiesSet()

  4. 自定义 init-method

  5. BeanPostProcessor#postProcessAfterInitialization

📌 AOP 代理对象在这一步产生

④ 容器刷新完成

• 发布 ContextRefreshedEvent

• Spring 容器进入 可用状态

四、Web 环境专属阶段(Spring Boot 特有)

启动内嵌 Web 容器

• Tomcat / Jetty / Undertow

• 注册:

• DispatcherServlet

• Filter

• Interceptor

📌 这里是 Spring Boot 和传统 Spring 最大的区别

传统 Spring:容器外有 Tomcat

Spring Boot:Tomcat 是 Bean

五、应用运行中(Running)

这时你可以:

• 接收 HTTP 请求

• 使用数据库

• 使用定时任务

• 发送 MQ 消息

典型运行期机制:

• @Scheduled

• @Async

• @EventListener

• AOP 切面

六、关闭阶段(Shutdown)

触发方式:

• JVM 关闭

• Ctrl + C

• 容器 stop

生命周期顺序:

  1. 发布 ContextClosedEvent

  2. 执行:

• @PreDestroy

• DisposableBean.destroy()

• destroy-method

  1. 关闭线程池、连接池

  2. 停止 Web 服务器

七、超重要:你真正该记住的 3 条线

① Bean 生命周期(面试 & 实战)

构造方法

→ 注入依赖

→ @PostConstruct

→ 初始化完成

→ 使用

→ @PreDestroy

② Spring Boot vs Spring

项目 Spring Spring Boot

启动方式 XML / 外部容器 main 方法

Web 服务器 外部 内嵌

自动配置 少 非常多

生命周期 一样 多了 Web 启动

③ 你写代码时该放在哪?

需求 正确位置

启动就执行 CommandLineRunner

Bean 初始化逻辑 @PostConstruct

修改 Bean 定义 BeanFactoryPostProcessor

拦截 Bean 创建 BeanPostProcessor

程序退出清理 @PreDestroy

你这个问题问得非常到位,而且不是"怎么用 Spring",而是在问它背后的概念根。我直接按你这个问题拆开说。

一、先给结论(非常重要)

在 Spring Boot 中,Bean 在启动之前「并不存在」

启动前存在的不是 Bean,而是 Bean 的"描述信息"。

换句话说:

Bean = 运行期对象

BeanDefinition = 设计图 / 蓝图 / 说明书

二、那"之前就有"的到底是什么?

不是 Bean

而是:BeanDefinition

Spring 启动时先收集一堆:

"未来要创建哪些对象"

"这些对象怎么 new"

"它们依赖谁"

"什么时候创建"

这就是 BeanDefinition

三、用英语概念精确对应(这是关键)

Spring 这套设计,对应的是几个非常标准的英语 / CS 概念:

1️⃣ Bean 本身是什么?

👉 Managed Object

• 普通 Java 对象:POJO

• 被容器托管:Managed Object

Bean = Object under IoC Container management

2️⃣ BeanDefinition 是什么?

👉 Metadata(元数据)

Spring 里的官方说法就是:

BeanDefinition = Metadata that describes a Bean

对应英语里的概念:

中文 英语

描述数据的数据 Metadata

对象说明书 Object Descriptor

构建规则 Configuration Model

📌 所以:

启动前"有"的是 metadata,不是 object

3️⃣ 为什么不是"类"?

你可能会下意识想:

"类不是早就编译好了吗?"

对,但:

• Class ≠ Bean

• Class 是 JVM 概念

• Bean 是 容器运行期概念

Spring 干的事是:

Class

→ 解析注解

→ 生成 BeanDefinition

→ 运行期 new 对象

→ 托管生命周期

四、类、BeanDefinition、Bean 的关系(非常重要)

.java 文件

↓(编译)

.class

↓(Spring 解析)

BeanDefinition ← 启动时存在

↓(refresh 阶段)

Bean(Object) ← 运行时存在

五、为什么要"先有定义,再有实例"?

这是一个非常经典的软件工程思想:

👉 Two-phase construction(两阶段构建)

英语里非常标准的概念:

Build plan first, build objects later

优点:

  1. 可以改"计划"(PostProcessor)

  2. 可以延迟创建(lazy)

  3. 可以做代理(AOP)

  4. 可以解决循环依赖

  5. 可以统一管理生命周期

六、类比你熟悉的东西(更好理解)

1️⃣ 数据库类比(你一定秒懂)

Spring 数据库

BeanDefinition 表结构

Bean 表中的一行数据

BeanFactory DB 引擎

2️⃣ Python / Flask 类比

@app.route("/user")

def user():

...

复制代码
•	装饰器阶段:只是注册规则
•	请求进来:才真正执行函数

👉 注册 ≠ 执行

👉 Definition ≠ Instance

七、那 Bean 是不是"凭空出现的"?

不是。

Bean 是 IoC Container 根据 BeanDefinition 在运行期 new 出来的对象

Object bean = createBean(beanDefinition);

八、为什么叫 Bean?英语里原本是什么意思?

这个也很有意思。

Bean 的原始含义:

• 豆子

• 小而独立的单元

• 可组合、可管理的组件

在 Java 世界里:

• JavaBean(1990s)

• 指符合规范的小组件

Spring 继承了这个词,但升级了含义:

JavaBean + IoC 管理 + 生命周期

九、一句话终极总结(给你一个"定锚")

Spring Boot 中,Bean 不是"之前就有"的东西

之前存在的是 Bean 的元数据(BeanDefinition)

Bean 是容器 refresh 过程中按定义创建出来的运行期对象

如果你愿意,我可以下一步直接帮你把:

• BeanDefinition 的字段逐个拆开

• 或者对比 C++ 对象模型 / Python 装饰器 / JVM ClassLoader

• 或者回答你一个更底层的问题:

👉 "如果没有 IoC,Bean 这个概念还存在吗?"

你现在更想往语言层、容器层还是哲学层继续挖?

相关推荐
Billow_lamb2 小时前
MyBatis Plus 中常用的插件列表
java·mybatis
程序猿DD2 小时前
人工智能如何改变 Anthropic 的工作方式
java·后端
C雨后彩虹2 小时前
任务总执行时长
java·数据结构·算法·华为·面试
weixin_462446232 小时前
用 Go 快速搭建一个 Coze (扣子)API 流式回复模拟接口(Mock Server)
开发语言·golang·状态模式
小鸡吃米…2 小时前
Python编程语言面试问题二
开发语言·python·面试
桦说编程2 小时前
Guava Forwarding系列类详解——装饰器模式实战
java·后端·设计模式
谁动了我的代码?3 小时前
QT<34> 利用线程池处理耗时任务以及回调函数的使用
开发语言·qt
柒.梧.3 小时前
数据结构:二叉排序树构建与遍历的解析与代码实现
java·开发语言·数据结构
李迟3 小时前
Golang实践录:接口文档字段转结构体定义
开发语言·golang