【Spring Boot启动流程底层源码详解】
文章目录
- Spring Boot启动流程详解
- 整体流程如下:
- 1. @SpringBootApplication注解作用
- 1.1 注解组合
- 1.2 注解功能
- 2. SpringApplication构造器执行流程
- 2.1 构造器调用链
- 2.2 构造器详细执行步骤
- 3. SpringApplication.run()方法执行流程
- 3.1 启动准备阶段
- 3.2 环境准备阶段
- 3.3 上下文创建和准备阶段
- 3.4 上下文刷新阶段
- 3.5 启动完成阶段
- 4. 与@SpringBootApplication的关联
- 4.1 主配置类的作用
- 4.2 注解处理时机
- 4.3 自动配置执行流程
- 5. 完整流程图
- 6. 关键扩展点
Spring Boot启动流程详解
整体流程如下:
1、静态方法run()调用
2、继续调用静态方法run()
3、new SpringApplication()对象
4、构造函数初始化
5、SpringApplicationshi实例run()调用
1. @SpringBootApplication注解作用
1.1 注解组合
@SpringBootConfiguration // 等同于@Configuration,标识这是配置类
@EnableAutoConfiguration // 启用自动配置机制
@ComponentScan // 启用组件扫描
public @interface SpringBootApplication {// ...
}
1.2 注解功能
- @SpringBootConfiguration: 将启动类标记为配置类,相当于Spring的@Configuration
- @EnableAutoConfiguration: 启用Spring Boot的自动配置机制,根据classpath自动配置Bean
- @ComponentScan: 扫描当前包及子包下的组件(@Component、@Service、@Repository等)
2. SpringApplication构造器执行流程
2.1 构造器调用链
// 1. 静态方法调用
SpringApplication.run(Application.class, args);// 2. 内部创建SpringApplication实例
new SpringApplication(primarySources).run(args);// 3. 执行构造器
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
2.2 构造器详细执行步骤
步骤1: 初始化基本属性
this.sources = new LinkedHashSet(); // 存储配置源
this.bannerMode = Mode.CONSOLE; // Banner显示模式
this.logStartupInfo = true; // 是否记录启动信息
this.addCommandLineProperties = true; // 是否添加命令行属性
this.addConversionService = true; // 是否添加转换服务
this.headless = true; // 无头模式
this.registerShutdownHook = true; // 注册关闭钩子
this.applicationContextFactory = ApplicationContextFactory.DEFAULT; //设置用于创建 ApplicationContext 的工厂(ApplicationContextFactory 接口
this.applicationStartup = ApplicationStartup.DEFAULT; // Instrumentation / 启动跟踪的钩子(ApplicationStartup 接口),用于收集启动事件/指标(例如用于可观察性、记录启动步骤)
步骤2: 设置资源加载器和主要源
this.resourceLoader = resourceLoader; // 设置资源加载器
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); // 设置主配置类
步骤3: 推断Web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
- NONE: 非Web应用
- SERVLET: 传统Servlet Web应用
- REACTIVE: 响应式Web应用
步骤4: 加载Bootstrap初始化器
根据 spring.factories 或 META-INF/spring/org.springframework.boot.BootstrapRegistryInitializer 等配置文件,
找到所有实现了 BootstrapRegistryInitializer 接口的类。
它会:
- 从类路径扫描这些实现类的全限定类名
- 通过反射实例化它们(可能还会做一些依赖注入或排序)
- 返回一个 List。
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class)
);
步骤5: 加载应用上下文初始化器
从 META-INF/spring.factories 或新版的 META-INF/spring/… 配置中找到实现了 ApplicationContextInitializer 接口的类名。
通过反射实例化它们,返回一个 List<ApplicationContextInitializer<?>>。
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)
);
步骤6: 加载应用监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)
);
步骤7: 推断主应用类
this.mainApplicationClass = this.deduceMainApplicationClass();
总结
加载对应的监听器或者初始化器的核心方法有什么?
SpringApplication#getSpringFactoriesInstances()
,然后依赖SpringFactoriesLoader.forDefaultResourceLocation().load()
方法,其中SpringFactoriesLoader.forDefaultResourceLocation()
创建一个 SpringFactoriesLoader 实例,里面指定了加载指定类的类加载器,默认去 META-INF/spring.factories 路径扫描配置文件。然后实例调用load去加载指定的Class对应的实现类
拓展
SpringFactoriesLoader 是什么?
-
SpringFactoriesLoader 是 Spring 提供的一个工具类(在org.springframework.core.io.support 包下)。
-
它的核心功能:
扫描并读取所有类路径下的 META-INF/spring.factories 配置文件,
并根据你指定的接口或抽象类,返回对应的实现类全限定名(然后可以反射实例化)。
3. SpringApplication.run()方法执行流程
3.1 启动准备阶段
步骤1: 创建启动计时器
Startup startup = SpringApplication.Startup.create();
步骤2: 配置关闭钩子
if (this.registerShutdownHook) {shutdownHook.enableShutdownHookAddition();
}
步骤3: 创建Bootstrap上下文
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
步骤4: 配置无头属性
this.configureHeadlessProperty(); // 设置java.awt.headless系统属性
步骤5: 获取运行时监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
3.2 环境准备阶段
步骤6: 准备应用参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
步骤7: 准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments
);
创建并加载应用运行环境(Environment),确保所有配置和 Profile 在容器刷新之前准备就绪,这时候容器就具备了某个配置文件对应的环境。
步骤8: 打印Banner
Banner printedBanner = this.printBanner(environment);
3.3 上下文创建和准备阶段
步骤9: 创建应用上下文
context = this.createApplicationContext();
根据webApplicationType创建相应的ApplicationContext:
- SERVLET: AnnotationConfigServletWebServerApplicationContext
- REACTIVE: AnnotationConfigReactiveWebServerApplicationContext
- NONE: AnnotationConfigApplicationContext
步骤10: 设置应用启动器
context.setApplicationStartup(this.applicationStartup);
步骤11: 准备上下文
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
prepareContext详细过程:
- 设置环境
- 执行ApplicationContextInitializer
- 触发监听器的contextPrepared事件
- 注册单例Bean(如applicationArguments、printedBanner)
- 加载配置源(主配置类等)
- 触发监听器的contextLoaded事件
3.4 上下文刷新阶段
步骤12: 刷新上下文
this.refreshContext(context);
这是核心步骤,执行Spring容器的refresh()方法:
- prepareRefresh(): 准备刷新
- obtainFreshBeanFactory(): 获取BeanFactory
- prepareBeanFactory(): 准备BeanFactory
- postProcessBeanFactory(): 后处理BeanFactory
- invokeBeanFactoryPostProcessors(): 执行BeanFactory后处理器
- registerBeanPostProcessors(): 注册Bean后处理器
- initMessageSource(): 初始化消息源
- initApplicationEventMulticaster(): 初始化事件多播器
- onRefresh(): 刷新特定上下文(如启动Web服务器)
- registerListeners(): 注册监听器
- finishBeanFactoryInitialization(): 完成Bean工厂初始化(实例化所有单例Bean)
- finishRefresh(): 完成刷新
步骤13: 刷新后处理
this.afterRefresh(context, applicationArguments);
3.5 启动完成阶段
步骤14: 标记启动完成
startup.started();
if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), startup);
}
步骤15: 触发started事件
listeners.started(context, startup.timeTakenToStarted());
步骤16: 调用Runner
this.callRunners(context, applicationArguments);
执行所有的ApplicationRunner和CommandLineRunner
步骤17: 触发ready事件并返回
if (context.isRunning()) {listeners.ready(context, startup.ready());
}
return context;
4. 与@SpringBootApplication的关联
4.1 主配置类的作用
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args); // Application.class作为primarySources传入}
}
4.2 注解处理时机
- 构造器阶段: Application.class被设置为primarySources
- prepareContext阶段: 主配置类被注册到容器中
- refresh阶段:
- @ComponentScan: 触发组件扫描,发现所有@Component等注解的类
- @EnableAutoConfiguration: 触发自动配置,加载所有符合条件的自动配置类
- @SpringBootConfiguration: 作为配置类被处理
4.3 自动配置执行流程
- EnableAutoConfigurationImportSelector被触发
- 读取META-INF/spring.factories中的自动配置类
- 根据条件注解(@ConditionalOnClass、@ConditionalOnMissingBean等)筛选
- 实例化符合条件的自动配置类
- 注册自动配置的Bean到容器中
5. 完整流程图
@SpringBootApplication↓
SpringApplication.run()↓
1. 创建SpringApplication实例├── 推断Web应用类型├── 加载初始化器和监听器└── 推断主应用类↓
2. 执行run()方法├── 创建Bootstrap上下文├── 准备环境(Environment)├── 创建应用上下文(ApplicationContext)├── 准备上下文├── 刷新上下文(refresh)│ ├── 组件扫描(@ComponentScan)│ ├── 自动配置(@EnableAutoConfiguration)│ └── 实例化所有Bean├── 调用Runner└── 启动完成
6. 关键扩展点
- ApplicationContextInitializer: 在上下文refresh之前执行
- ApplicationListener: 监听应用启动过程中的各种事件
- BeanFactoryPostProcessor: 在Bean定义加载后、Bean实例化前执行
- BeanPostProcessor: 在Bean实例化过程中执行
- ApplicationRunner/CommandLineRunner: 在应用启动完成后执行
这个流程确保了Spring Boot应用的完整启动,从注解解析到Bean创建再到应用就绪,每个步骤都有明确的职责和执行时机。
本人水平有限,有错的地方还请批评指正。
什么是精神内耗?
简单地说,就是心理戏太多,自己消耗自己。
所谓:
言未出,结局已演千百遍;
身未动,心中已过万重山;
行未果,假想灾难愁不展;
事已闭,过往仍在脑中演。