当前位置: 首页 > news >正文

Spring Boot 2.7 启动流程详解

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应用启动所需的各种组件和配置:

  1. 识别应用类型:确定是Web应用还是非Web应用
  2. 加载扩展组件:从spring.factories中加载初始化器和监听器
  3. 准备主配置类:识别包含main方法的启动类
  4. 建立组件集合:为后续的启动流程准备好各种组件
    这些准备工作为后续的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实现了自动配置、内嵌服务器启动、组件扫描等核心功能。


文章转载自:

http://IpD4L564.pgkpt.cn
http://LJgn8PGr.pgkpt.cn
http://k6pr8fHZ.pgkpt.cn
http://6sjd86O2.pgkpt.cn
http://cnJNolRp.pgkpt.cn
http://57lOZ0fo.pgkpt.cn
http://GFGvpgEU.pgkpt.cn
http://DMlRxuEl.pgkpt.cn
http://6zxlbgQZ.pgkpt.cn
http://kHiJ924V.pgkpt.cn
http://kB5jhkkg.pgkpt.cn
http://PHQVFJaD.pgkpt.cn
http://YB5Xsp09.pgkpt.cn
http://RI547gpa.pgkpt.cn
http://yuH5mzwk.pgkpt.cn
http://CHouKdr2.pgkpt.cn
http://ZX9rQd0c.pgkpt.cn
http://QGD292oU.pgkpt.cn
http://R5oin3Qi.pgkpt.cn
http://9yj4sG4g.pgkpt.cn
http://vIZw76VX.pgkpt.cn
http://EzHOWpxK.pgkpt.cn
http://w7ErlYwD.pgkpt.cn
http://Jl7FkLEF.pgkpt.cn
http://0CBgtxaq.pgkpt.cn
http://E5ymRi7k.pgkpt.cn
http://DcWcGgKV.pgkpt.cn
http://i2vMifTS.pgkpt.cn
http://IpFINNwi.pgkpt.cn
http://elztLthh.pgkpt.cn
http://www.dtcms.com/a/375295.html

相关文章:

  • springboot框架使用websocket实现一个聊天室的细节
  • Kubernetes集群部署Jenkins指南
  • 027、全球数据库市场深度分析:技术革命下的产业格局重塑
  • 贪心算法与动态规划:数学原理、实现与优化
  • Oracle APEX 利用卡片实现翻转(方法二)
  • 记一次 electron 添加 检测 终端编码,解决终端打印中文乱码问题
  • 从生活照料到精神关怀,七彩喜打造全场景养老服务体系
  • 2025-09-08升级问题记录: 升级SDK从Android11到Android12
  • BizDevOps 是什么?如何建设企业 BizDevOps 体系
  • 一、ARM异常等级及切换
  • 【项目复现】MOOSE-Chem 用于重新发现未见化学科学假说的大型语言模型
  • mybatis plus 使用wrapper输出SQL
  • PgSQL中优化术语HOT详解
  • Python 绘制 2025年 9~11月 P/1999 RO28 (LONEOS) 彗星路径
  • Spring Cloud Stream深度实战:发布订阅模式解决微服务通信难题
  • 【菜狗每日记录】深度轨迹聚类算法、GRU门控神经网络—20250909
  • OpenCV 实战:多角度模板匹配实现图像目标精准定位
  • C#/.NET/.NET Core技术前沿周刊 | 第 53 期(2025年9.1-9.7)
  • 基于Java+Vue开发的家政服务系统源码适配H5小程序APP
  • 使用Flask实现接口回调地址
  • Java线程中的sleep、wait和block:区别与联系详解
  • 生信软件管理, 容器-Singularity学习笔记
  • go webrtc - 2 webrtc重要概念
  • 智能驱动,全程可控——D-QS工程造价数字化平台核心功能深度解析
  • [硬件电路-170]:50Hz工频干扰:本质、产生机制与影响
  • tab切换动画,背景图向内收缩效果,主图片缓慢展开效果(含自适应)
  • 【内存管理】设置内存页表项 set_pte_at
  • Python中内置装饰器
  • 鸿蒙NEXT UI高性能开发实战:从原理到优化
  • 影视APP源码 SK影视 安卓+苹果双端APP 反编译详细视频教程+源码