SpringBoot 的启动引导类真的是 XXApplication 吗?

1. 引导类的“名”与“实”:为什么大家都叫 Application?

Spring Boot 提倡“约定优于配置”的理念。XXXApplication 这个命名方式,本质上是官方文档和绝大多数教程为了统一、规范和易于识别而推广的一种约定。它就像是一个显眼的“入口”路牌,告诉所有开发者:“嘿,项目的起点就在这里!”

所以,回答你的第一个问题:“真的是 XXApplication 吗?”——在约定上,是的;但在语法上,不是。 它的真实身份,是一个被 @SpringBootApplication 注解标记的、包含 main 方法的普通 Java 类。

2. 拆解引导类的“灵魂”:@SpringBootApplication

引导类的强大,并非源自它的名字,而是它头顶的那个神奇的注解——@SpringBootApplication。这个注解本身并不复杂,但它是一个“三合一”的组合注解,是开启 Spring Boot 世界的钥匙。

java

// 一个典型的Spring Boot引导类 @SpringBootApplication // <-- 核心中的核心 public class MyAwesomeApp { public static void main(String[] args) { SpringApplication.run(MyAwesomeApp.class, args); } }

让我们一层层剥开 @SpringBootApplication 的外衣 :

  • @SpringBootConfiguration:它本身又是一个 @Configuration 注解的“马甲”。这意味着,我们的引导类本身就是一个配置类。你可以在这个类里通过 @Bean 注解向 Spring 容器注册额外的 Bean。
  • @ComponentScan:这就是 “组件扫描” 注解。它的默认扫描范围是引导类所在包及其所有子包 。这就是为什么我们通常建议将引导类放在项目的根目录下,比如 com.example.myproject,这样它就能自动扫描到 com.example.myproject.controllercom.example.myproject.service 等子包下的 @Controller@Service 等组件。
  • @EnableAutoConfiguration:这是整个 Spring Boot 框架的灵魂所在,负责开启自动配置。它会根据你在项目类路径中添加的依赖(比如 spring-boot-starter-web),自动推断并配置你需要的 Bean。例如,你添加了 web starter,它就会自动帮你配置好 DispatcherServlet 和内嵌的 Tomcat 服务器。

3. 深度剖析:SpringApplication.run() 里到底发生了什么?

引导类的 main 方法只做了一件事:委托给 SpringApplication.run() 静态方法。但这一行代码背后,是一套极其精巧的启动流程。整个过程可以分为两大阶段:初始化 和 运行 。

3.1 初始化阶段:准备舞台

当调用 run 方法时,会先创建一个 SpringApplication 实例。在这个构造函数中,Spring Boot 会完成一系列“侦查”工作:

  1. 推断应用类型:检查类路径下是存在 DispatcherServlet(传统 Servlet)还是 DispatcherHandler(WebFlux),从而确定应用类型是响应式 Web 还是 Servlet Web,抑或是非 Web 应用 。
  2. 加载初始化器:从 META-INF/spring.factories 文件中加载所有配置的 ApplicationContextInitializer。这些初始化器用于在 Spring 容器刷新前对其进行定制。
  3. 加载监听器:同样从 spring.factories 中加载所有配置的 ApplicationListener。这些监听器会监听启动过程中的不同阶段事件,并执行相应动作 。
  4. 推断主启动类:通过堆栈信息找到含有 main 方法的类,也就是我们的引导类。
3.2 运行阶段:正式开演

初始化完成后,真正的 run() 逻辑开始执行,整个过程充满了事件驱动 。

  • ApplicationStartingEvent:发布应用启动事件。
  • 准备环境 prepareEnvironment():创建 Environment 对象,并加载系统环境变量、JVM 参数以及最重要的 application.properties/yml 配置文件。完成后发布 ApplicationEnvironmentPreparedEvent 。
  • 创建上下文 createApplicationContext():根据初始化阶段推断的应用类型,创建对应的 ApplicationContext(IoC 容器)。例如,对于 Servlet Web 应用,会创建 AnnotationConfigServletWebServerApplicationContext 。
  • 准备上下文 prepareContext():将 Environment 绑定到上下文,执行所有已加载的 ApplicationContextInitializer,并通过 @ComponentScan 将引导类本身作为一个配置源,将其 Bean 定义加载到容器中。之后发布 ApplicationPreparedEvent
  • 刷新上下文 refreshContext():这是 Spring 框架最核心的方法。它会完成所有 Bean 的实例化、依赖注入、初始化,以及最重要的——触发自动配置。Tomcat 等内嵌 Web 服务器也是在此步骤中启动的 。
  • 启动后动作 afterRefresh():执行所有实现了 ApplicationRunner 和 CommandLineRunner 接口的回调类,用于执行启动后的初始化任务 。
  • ApplicationReadyEvent:发布应用已准备就绪事件,应用正式对外提供服务。

4. 进阶探索:引导类还可以这样玩

除了最简单的 SpringApplication.run(),我们还可以通过创建和定制 SpringApplication 对象来实现更精细的控制 。

java

@SpringBootApplication public class CustomizedApp { public static void main(String[] args) { // SpringApplication app = new SpringApplication(CustomizedApp.class); SpringApplication app = new SpringApplication(); app.setPrimarySources(Set.of(CustomizedApp.class)); app.setBannerMode(Banner.Mode.OFF); // 关闭Banner app.setDefaultProperties(Collections .singletonMap("server.port", "8081")); // 设置默认端口 // 添加自定义监听器 app.addListeners(new MyApplicationListener()); ConfigurableApplicationContext context = app.run(args); // ... 可以获取context做一些事情 } }

5. 终极揭秘:Jar 包启动的“障眼法”

当我们把 Spring Boot 项目打成可执行 Jar 包并运行(java -jar myapp.jar)时,引导类的角色变得更加有趣。你可能会想,我明明设置了 MyAwesomeApp 作为主类,为什么启动时还能看到 Spring Boot 的 Banner?

答案是,真正的“引导”另有其人。Spring Boot 的 Jar 包结构是特殊的,它的 META-INF/MANIFEST.MF 文件里定义了真实的入口 :

properties

Main-Class: org.springframework.boot.loader.launch.JarLauncher Start-Class: com.example.myproject.MyAwesomeApp

  • Main-Class:这里配置的是 JarLauncher,它是 Spring Boot 自己提供的引导程序。它的工作是创建自定义的类加载器,从 Jar 包内部的 BOOT-INF/lib 目录下加载所有依赖的 Jar 包。
  • Start-Class:这里才指向我们编写的引导类 MyAwesomeAppJarLauncher 在准备好类加载器后,会通过反射调用 MyAwesomeApp 的 main 方法,从而正式启动我们的 Spring Boot 应用 。

所以,从物理 Jar 包层面看,真正的“引导类”是 JarLauncher;而从业务逻辑层面看,MyAwesomeApp 仍然是当之无愧的“启动引导类”。这是一种巧妙的分层设计。

总结

现在我们可以清晰地回答你的问题了:

  1. 命名约定XXApplication 是官方推荐的命名习惯,但不是强制规定。
  2. 本质核心:引导类的本质是一个被 @SpringBootApplication 注解标记的配置类,它作为组件扫描和自动配置的起点。
  3. 启动原理:引导类通过 SpringApplication.run() 触发一系列复杂的初始化、事件发布和容器刷新流程,最终启动内嵌服务器,完成应用自举。
  4. 终极真相:在打包成可执行 Jar 后,物理上的引导类是 JarLauncher,它负责创建环境并调用我们定义的业务引导类。

Read more

我用Go只花了13天开发出可自我演进的AI助手

在OpenClaw刚发布的当天我就疯狂地玩了一段时间,开始我还真是被它给惊艳到了。觉得它很聪明貌似好多的活会自动在后台完成至少不需要我与它反复沟通,而且还可以与飞书通信,我老婆还说我像个拿到了心爱玩具的孩子。那当然了,从业27年了已经很久没有因为某项技术而让我如此兴奋了!我感觉它能做的很多事情,很多以前想做而又分身乏术做不了的事。 但我不幸的是我发现它用得越久就变得越来越“蠢”似乎思维速度在持续地退化,而且很多对话都是直接不回复,最严重的一次是发现QWen Chat上token好像都耗光了,当时看TUI的统计是Token用量为680% / 100%! 然后,就没有然后了,直接罢工,虽然OpenClaw的更新很快,我还是从源码安装每天都去pull新的内容,但这几天“教”它的所有东西都没用了。 这两天下来的感觉是兴奋属于高开低走,它的实际智能还比不上AI开发工具,可能我是没能用上Opus吧,据说只要付得起钱一天烧个100~200刀用Opus可以玩得很爽,我无法验证这点,但从实际运行来看,OpenClaw的理念虽好,但是个烧钱怪!而且根本不是给中国人设计的,整个技能生态度极度不友好,我们

By Ne0inhk
【MySQL基础】MySQL表操作全面指南:从创建到管理的深度解析

【MySQL基础】MySQL表操作全面指南:从创建到管理的深度解析

MySQL学习: https://blog.ZEEKLOG.net/2301_80220607/category_12971838.html?spm=1001.2014.3001.5482 前言: 在上一篇我们学习了库的创建和使用,表是其存储数据的核心结构。本文将全面讲解MySQL中关于表的各项操作,包括创建、修改、删除等,并深入探讨相关知识点和注意事项。 表的基本概念 在MySQL中,表是存储数据的主要对象,由行和列组成。理解表的基本结构对数据库设计至关重要。 表的主要组成部分 组成部分描述表名表的唯一标识符,遵循命名规则列(字段)表的垂直结构,定义数据的类型和约束行(记录)表的水平结构,实际存储的数据主键唯一标识表中每一行的列或列组合索引提高查询性能的数据结构约束 保证数据完整性的规则 创建表 基本语法 CREATE TABLE [IF NOT EXISTS] table_name ( column1 datatype [constraints]

By Ne0inhk
【JavaEE】springMVC返回Http响应

【JavaEE】springMVC返回Http响应

目录 * 一、返回页面 * 二、@Controller和@ResponseBody与RestController区别 * 三、返回HTML代码⽚段 * 四、返回JSON * 五、HttpServletResponse设置状态码 * 六、设置Header * 6.1 HttpServletResponse设置 * 6.2 @RequestMapping设置 一、返回页面 步骤如下: 我们先要在static目录下创建我们的HTML文件。 HTML源码: <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Index</title&

By Ne0inhk
zoxide 开源鸿蒙 PC 生态适配实战:Rust 交叉编译与 HNP 打包完整指南

zoxide 开源鸿蒙 PC 生态适配实战:Rust 交叉编译与 HNP 打包完整指南

zoxide 开源鸿蒙 PC 生态适配实战:Rust 交叉编译与 HNP 打包完整指南 前言:为什么要把 zoxide 引入开源鸿蒙 PC 生态? 作为 Linux 终端下广受欢迎的智能目录跳转工具,zoxide 凭借关键词模糊匹配 + 访问频率排序的核心优势,彻底解决了传统 cd 命令需记忆冗长路径、逐级跳转的痛点,成为开发者与运维人员提升终端效率的必备工具。随着鸿蒙PC生态的快速发展,终端命令行工具的丰富度成为提升用户体验的关键环节。为让开源鸿蒙 PC 用户也能享受到 zoxide 的高效便捷。 本文基于 Rust 交叉编译技术与开源鸿蒙 HNP 规范,详细拆解 zoxide 从源码拉取、构建脚本配置、交叉编译打包,到设备端安装验证的完整适配流程。文中不仅提供可直接复用的配置文件与命令代码,还汇总了适配过程中常见的 Rust 编译、链接器兼容等问题及解决方案,为开发者提供一套低成本、高可复用的开源鸿蒙

By Ne0inhk