作者:CodeStats
一个专注分享Java底层原理、自研框架实战干货的技术博主。如果觉得内容实用,欢迎点赞 + 收藏 + 关注!
📚 相关阅读(造轮者必备)
本文的思考,源于我自己动手手写 Tomcat + 自研 IoC 容器的经历。如果你也对“造轮子”感兴趣,推荐搭配阅读:
手写Tomcat + 自研IoC:我用AI一周实现了一个完整的Java Web框架-CSDN博客
这篇文章从零实现了一个极简 Web 服务器和依赖注入容器,与本文的 Spring Boot 原理形成“实战 vs 框架”的对照。从 Spring 到 Spring Boot 再到 Spring Cloud:自动配置原理与动态代理深度解析(附自研框架 Codestats 实战)-CSDN博客
帮你建立从 Spring 基础到微服务生态的完整认知链路。
📑 目录
一、一句话说清:Spring Boot 启动就干两件事
二、内嵌 Tomcat 和 DispatcherServlet 如何搭上线
三、核心思想:依赖注入才是灵魂
四、BeanFactoryPostProcessor vs BeanPostProcessor(区别 + 生效时机)
五、12 步 refresh() 的“分段解读”
六、依赖注入什么时候真正发生?
七、事件机制是如何实现的
八、总结:从配置到请求,链路终于通了
一、一句话说清:Spring Boot 启动就干两件事
从造轮者的角度看,Spring Boot 启动时只做两件核心工作:
构建 IoC 容器:收集所有 Bean 的(BeanDefinition),按规则创建对象并完成依赖注入。
挂载 Web 能力:启动内嵌 Web 服务器(如 Tomcat),并将请求分发核心 DispatcherServlet 注册进去,让它能把 HTTP 请求转给已经建好的 Controller。
这两件事有严格顺序:先有 IoC 容器,后有 Web 服务器。因为服务器需要的组件(如 Controller、HandlerMapping)都来自容器。
最朴实的比喻:先把家里收拾利索(所有 Bean 造好、依赖引好),再开门营业(启动 Tomcat,接收请求)。
二、内嵌 Tomcat 和 DispatcherServlet 如何搭上线?
2.1 文字说明
这一步是许多人的盲区,其实拆开来看非常简单:
1. Tomcat 是谁启动的?
Spring Boot 在refresh()的第 9 步onRefresh()中,调用ServletWebServerApplicationContext#onRefresh()→createWebServer()→ 实例化 Tomcat,绑定端口,启动。
2. DispatcherServlet 是谁创建的?DispatcherServletAutoConfiguration自动配置类中有一个@Bean返回DispatcherServlet实例,同时还会创建ServletRegistrationBean(实现了ServletContextInitializer接口)。
3. 怎么注册到 Tomcat?
Tomcat 启动后会回调所有ServletContextInitializer,ServletRegistrationBean就把DispatcherServlet添加到 Tomcat 的ServletContext中,并映射到"/"。
最终效果:http://host:port/xxx→ Tomcat → DispatcherServlet → 你的 Controller 方法。
没有魔法:Tomcat 被包装成一个普通 Bean,
ServletRegistrationBean只是一个“注册器”。
三、核心思想:依赖注入才是灵魂
无论看多少遍 Spring 源码,请记住这条主线:
配置信息(注解/配置类) → BeanDefinition(元数据) → Bean 实例 → 依赖注入
三个最关键的组件:
BeanDefinition:Bean 的信息,记录类名、作用域、构造参数、依赖关系等。
BeanFactoryPostProcessor:这阶段动手脚(修改 BeanDefinition)。例如
ConfigurationClassPostProcessor解析@ComponentScan、@Import、@Bean。BeanPostProcessor:在 Bean 实例化之后,对“成品”进行加工(
@Autowired注入、AOP 代理)。
很多初学者混淆后两者,下一节专门对比。
四、BeanFactoryPostProcessor vs BeanPostProcessor(区别 + 生效时机)
| 接口 | 操作对象 | 执行阶段 | 典型例子 |
|---|---|---|---|
| BeanFactoryPostProcessor | BeanDefinition(元数据) | 实例化任何 Bean之前,refresh()第 5 步invokeBeanFactoryPostProcessors | ConfigurationClassPostProcessor(解析注解、自动配置) |
| BeanPostProcessor | Bean实例(已经 new 出来的对象) | 实例化之后,初始化前后;第 6 步注册,实际调用在getBean()过程中 | AutowiredAnnotationBeanPostProcessor(处理@Autowired) |
一句话总结:
BeanFactoryPostProcessor改图纸,BeanPostProcessor修成品。
前者在盖楼前改设计图,后者在楼盖好后刷墙布线。
自动配置为什么不用写<bean>?因为ConfigurationClassPostProcessor读到@SpringBootApplication后,主动去META-INF/.../AutoConfiguration.imports里拉了一堆配置类,全变成了BeanDefinition。
五、12 步 refresh() 的“分段解读”(剥去繁琐,只留骨架)
AbstractApplicationContext.refresh()共 12 步,分四个阶段理解:
🔹 阶段一:准备 & 获取 BeanFactory(第 1~4 步)
prepareRefresh:校验必要属性。obtainFreshBeanFactory:创建DefaultListableBeanFactory(存 BeanDefinition 的仓库)。prepareBeanFactory:配置类加载器、表达式解析器,加几个系统级 BeanPostProcessor。postProcessBeanFactory:扩展钩子(Web 环境注册 Scope)。
产出:一个空的、配置好的 BeanFactory。
🔹 阶段二:加载 & 修改 BeanDefinition(第 5 步)
invokeBeanFactoryPostProcessors:执行所有 BeanFactoryPostProcessor。ConfigurationClassPostProcessor解析主类上的@ComponentScan、@Import、@Bean及自动配置,生成大量新 BeanDefinition 并注册。
产出:
beanDefinitionMap里填满了所有 Bean 的定义信息。
🔹 阶段三:注册“装修队”(第 6 步)
registerBeanPostProcessors:找出所有BeanPostProcessor,实例化并存入工厂列表。
这些“装修队”会在每个 Bean 实例化后被调用,完成@Autowired注入、AOP 等。
注意:只注册,不调用。真正调用在
getBean()过程中。
🔹 阶段四:基础设施 & 事件 & 实例化(第 7~12 步)
initMessageSource:国际化。initApplicationEventMulticaster:事件广播器。onRefresh:启动内嵌 Tomcat(见第二部分)。registerListeners:注册事件监听器。finishBeanFactoryInitialization:实例化所有非懒加载的单例 Bean,过程中触发 BeanPostProcessor 完成依赖注入。finishRefresh:发布ContextRefreshedEvent,启动完成。
最核心的是
finishBeanFactoryInitialization—— 把图纸变成真实对象,并灌入依赖。
六、依赖注入什么时候真正发生?
答案:在finishBeanFactoryInitialization()中调用getBean()创建每个单例 Bean 的时候。
简化流程:
getBean(beanName)→ 从beanDefinitionMap取出 BeanDefinition。根据 BeanDefinition 实例化对象(构造器或工厂方法)。
执行 BeanPostProcessor 的钩子,其中
AutowiredAnnotationBeanPostProcessor扫描@Autowired字段/方法,从容器取出依赖并反射赋值。执行初始化方法(
@PostConstruct、InitializingBean)。返回成品 Bean 存入
singletonObjects缓存。
依赖注入不是独立的一步,而是嵌在getBean()过程中,由 BeanPostProcessor 驱动。
七、事件机制是如何实现的(附简单代码示例)
Spring 的事件机制基于发布-订阅模式,允许 Bean 之间解耦通信。
核心组件
事件(ApplicationEvent):继承
ApplicationEvent,封装信息。监听器(ApplicationListener):监听特定事件,执行逻辑。
广播器(ApplicationEventMulticaster):负责派发事件给所有匹配的监听器。
发布者:注入
ApplicationEventPublisher,调用publishEvent()。
简单代码示例
java
// 1. 自定义事件 public class MyCustomEvent extends ApplicationEvent { private final String message; public MyCustomEvent(Object source, String message) { super(source); this.message = message; } public String getMessage() { return message; } } // 2. 监听器(方式一:实现接口) @Component public class MyEventListener implements ApplicationListener<MyCustomEvent> { @Override public void onApplicationEvent(MyCustomEvent event) { System.out.println("收到事件:" + event.getMessage()); } } // 或者方式二:使用 @EventListener(更简洁) @Component public class AnotherListener { @EventListener public void handleEvent(MyCustomEvent event) { System.out.println("@EventListener 收到:" + event.getMessage()); } } // 3. 发布事件(任意 Bean 中) @Service public class EventPublisherService { @Autowired private ApplicationEventPublisher publisher; public void send(String msg) { publisher.publishEvent(new MyCustomEvent(this, msg)); } }执行流程:
Spring 在
initApplicationEventMulticaster()中创建广播器(默认为SimpleApplicationEventMulticaster)。registerListeners()将所有ApplicationListenerBean 注册到广播器。调用
publishEvent()时,广播器同步或异步派发事件给所有监听器。finishRefresh()最后还会发布ContextRefreshedEvent,通知容器已启动。
事件机制完全依赖 IoC 容器:监听器本身也是 Bean,广播器从容器获取监听器列表。这正是依赖注入思想的延伸。
八、总结:从配置到请求,链路终于通了
最简化时序链:
text
主类 @SpringBootApplication ↓ (解析阶段) ConfigurationClassPostProcessor 读取自动配置、扫描 @Component ↓ (生成 BeanDefinition) beanDefinitionMap 塞满Bean信息 ↓ (finishBeanFactoryInitialization) 实例化每个 Bean,通过 BeanPostProcessor 完成依赖注入 ↓ (onRefresh) Tomcat 启动,DispatcherServlet 注册 ↓ HTTP 请求 → Tomcat → DispatcherServlet → 你的 Controller
很多人觉得 Spring Boot 复杂,是因为把“自动配置”、“依赖注入”、“Web 服务器启动”、“事件机制”等多件事搅在一起。其实它们是有序串联的:先有 Bean 定义,再有 Bean 实例,再有依赖填充和事件广播器,最后挂上 Web 服务器。
如果你能画出这个顺序,再回头看refresh()的 12 步,每一步都有它明确的位置和作用。
本文以“造轮者”的视角,只希望用最朴素的逻辑帮你串起整个启动流程。
如果觉得有帮助,别忘了点赞、收藏、加关注,我是 CodeStats,下篇聊 AOP 到底是怎么“切”进去的