Spring Boot 2.7 启动流程详解
这就是Spring Boot 2.7版本完整的启动流程,通过这个流程Spring Boot实现了自动配置、内嵌服务器启动、组件扫描等核心功能。这是Spring Boot应用的入口方法,返回一个可配置的应用上下文对象。设置资源加载器,如果传入null,则在需要时会创建默认的资源加载器。通过检查调用栈,找到方法名为"main"的类作为主应用类。SpringApplication 构造函数详解。
·
1.Spring Boot 2.7中SpringApplication构造函数的实例化流程
源代码:
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details). The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringApplication 构造函数详解
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
这个构造函数接收两个参数:
- resourceLoader:资源加载器,用于加载应用资源
- primarySources:主要源类,通常是包含main方法的启动类
1.1. 资源加载器设置
this.resourceLoader = resourceLoader;
设置资源加载器,如果传入null,则在需要时会创建默认的资源加载器。
1.2. 主要源类验证和存储
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
- 使用断言确保primarySources不为null
- 将主要源类存储在LinkedHashSet中,保证唯一性和插入顺序
1.3. Web应用类型推断
this.webApplicationType = WebApplicationType.deduceFromClasspath();
通过WebApplicationType.deduceFromClasspath()方法推断Web应用类型:
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", null)
&& !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", null)
&& !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
有三种类型:
- NONE:非Web应用
- SERVLET:基于Servlet的Web应用(如Tomcat、Jetty)
- REACTIVE:响应式Web应用(如WebFlux)
1.4. Bootstrap注册初始化器设置
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
获取并设置Bootstrap注册初始化器:
- BootstrapRegistryInitializer是在引导阶段使用的初始化器
- 通过getSpringFactoriesInstances方法从META-INF/spring.factories中加载所有BootstrapRegistryInitializer实现类
1.5. 应用上下文初始化器设置
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
获取并设置应用上下文初始化器:
- ApplicationContextInitializer用于在刷新应用上下文之前进行自定义初始化
- 从META-INF/spring.factories中加载所有ApplicationContextInitializer实现类
通过setInitializers方法设置到SpringApplication中
1.6. 应用监听器设置
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
获取并设置应用监听器:
- ApplicationListener用于监听Spring应用中的各种事件
- 从META-INF/spring.factories中加载所有ApplicationListener实现类
通过setListeners方法设置到SpringApplication中
1.7. 主应用类推断
this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
通过检查调用栈,找到方法名为"main"的类作为主应用类。
1.8 getSpringFactoriesInstances 方法解析
getSpringFactoriesInstances是关键方法,它负责从META-INF/spring.factories文件中加载指定类型的实例:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, null);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 使用SpringFactoriesLoader加载所有实现类
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
总结
SpringApplication的实例化过程主要是为了准备Spring Boot应用启动所需的各种组件和配置:
- 识别应用类型:确定是Web应用还是非Web应用
- 加载扩展组件:从spring.factories中加载初始化器和监听器
- 准备主配置类:识别包含main方法的启动类
- 建立组件集合:为后续的启动流程准备好各种组件
这些准备工作为后续的run()方法执行奠定了基础,使得Spring Boot能够根据不同的应用类型和配置进行相应的初始化。
2.Spring Boot 2.7 启动流程详解
源码:
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
这是Spring Boot应用的入口方法,返回一个可配置的应用上下文对象。
2.1. 启动计时和引导上下文创建
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
- 记录启动开始时间,用于计算启动耗时
- 创建引导上下文(Bootstrap Context),用于在主应用上下文创建之前初始化一些关键组件
2.2. 初始化应用上下文和配置
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
- 声明主应用上下文变量
- 配置headless属性,设置系统在无鼠标键盘等外设环境下是否正常运行
2.3. 启动监听器处理
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
- 获取运行监听器,这些监听器会监听启动过程中的各个阶段事件
- 触发starting事件,通知所有监听器Spring Boot应用正在启动
2.4. 环境准备阶段
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
- 解析命令行参数
- 准备环境:创建并配置应用环境,包括读取配置文件、处理Profile等
- 配置是否忽略Bean信息
- 打印Banner(启动时显示的ASCII艺术字)
2.5. 应用上下文创建和配置
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
- 创建应用上下文,根据应用类型选择合适的上下文实现
- 设置应用启动跟踪器
- 准备上下文:将环境、监听器、命令行参数等注册到上下文中
2.6. 刷新上下文(核心步骤)
refreshContext(context);
afterRefresh(context, applicationArguments);
刷新应用上下文,这是最重要的步骤,包括:
- 初始化Bean工厂
- 调用BeanFactoryPostProcessor
- 注册BeanPostProcessor
- 初始化MessageSource
- 初始化事件广播器
- 注册监听器
- 实例化所有非懒加载的单例Bean
- 发布上下文刷新完成事件
刷新后的回调处理
2.7. 启动完成通知
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
- 计算启动耗时
- 记录启动日志信息
- 触发started事件,通知监听器应用已启动
- 调用Runner:执行所有实现ApplicationRunner或CommandLineRunner接口的Bean
2.8. 应用就绪状态
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
- 计算从启动到就绪的总耗时
- 触发ready事件,通知监听器应用已准备就绪,可以处理请求
2.9. 异常处理
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
- 如果启动过程中发生异常,调用异常处理方法并抛出IllegalStateException
关键特性
- 事件驱动:通过SpringApplicationRunListeners在整个启动过程中发布各种事件
- 模块化设计:每个步骤都有明确的职责,便于扩展和定制
- 异常处理:完善的异常处理机制确保启动失败时能正确清理资源
- 性能监控:记录启动时间,便于性能分析
这就是Spring Boot 2.7版本完整的启动流程,通过这个流程Spring Boot实现了自动配置、内嵌服务器启动、组件扫描等核心功能。
更多推荐
所有评论(0)