不管是刚写的 HelloWorld 项目,还是复杂的企业级 SpringBoot 应用,总有一个"独一无二"的主启动类------就是那个带 main 方法、加了 @SpringBootApplication 注解的类,点一下运行,整个项目就活了。
很多同学只知道"点一下就能跑",但根本不知道这个类到底干了啥,@SpringBootApplication 这个注解又藏了多少"猫腻"。今天就扒开它的底层逻辑,把主启动类的核心讲透,看完你就知道:SpringBoot 一键启动的秘密,全在这了。
主启动类长啥样?
先贴一个最标准的 SpringBoot 主启动类代码,咱们从这行代码开始拆解:
go
1importorg.springframework.boot.SpringApplication;
2importorg.springframework.boot.autoconfigure.SpringBootApplication;
3
4// 核心注解:这是 SpringBoot 应用的入口标识
5@SpringBootApplication
6publicclassFirstSpringbootApiApplication{
7
8// 程序入口:main 方法(和普通 Java 程序的 main 方法一样)
9publicstaticvoidmain(String[] args){
10// 核心方法:启动 SpringBoot 应用
11SpringApplication.run(FirstSpringbootApiApplication.class, args);
12}
13}
看似只有几行代码,实则包含了 3 个核心动作:
-
@SpringBootApplication:标记这是 SpringBoot 应用,触发自动配置、包扫描;
-
main方法:Java 程序的入口,JVM 启动后先执行这个方法;
-
SpringApplication.run():启动 Spring 容器、加载自动配置、启动内置服务器(Tomcat)。
咱们先拆最核心的 @SpringBootApplication 注解------它不是"单个注解",而是"组合注解",这是 SpringBoot 简化配置的关键。
核心拆解:@SpringBootApplication = 3 个注解的"合体"
按住 Ctrl 点击 @SpringBootApplication 注解(IDEA 里),能看到它的源码:
go
1@Target({ElementType.TYPE})
2@Retention(RetentionPolicy.RUNTIME)
3@Documented
4@Inherited
5// 核心注解1:开启自动配置
6@EnableAutoConfiguration
7// 核心注解2:组件扫描(扫描当前包及子包的 Bean)
8@ComponentScan(excludeFilters ={
9@Filter(type =FilterType.CUSTOM, classes ={TypeExcludeFilter.class}),
10@Filter(type =FilterType.CUSTOM, classes ={AutoConfigurationExcludeFilter.class})
11})
12// 核心注解3:配置类(允许在类里定义 @Bean)
13@Configuration
14public@interfaceSpringBootApplication{
15// 省略可选配置(比如排除某些自动配置类)
16}
一句话总结:@SpringBootApplication = @Configuration + @ComponentScan + @EnableAutoConfiguration,这 3 个注解各管一摊事,缺一个都不行。
注解1:@Configuration------让主启动类变成"配置类"
这个注解你可以理解为:把普通的 Java 类变成 Spring 的"配置文件"(替代传统 Spring 的 XML 配置)。
在主启动类里,你可以加 @Bean 注解定义组件(比如自定义的拦截器、数据源),Spring 启动时会扫描并创建这些 Bean。
比如:
go
1@SpringBootApplication
2publicclassFirstSpringbootApiApplication{
3
4// 定义一个 Bean,Spring 容器会管理这个对象
5@Bean
6publicLoginInterceptorloginInterceptor(){
7returnnewLoginInterceptor();
8}
9
10publicstaticvoidmain(String[] args){
11SpringApplication.run(FirstSpringbootApiApplication.class, args);
12}
13}
注解2:@ComponentScan------SpringBoot 知道该扫描哪些类
这个注解的核心作用:扫描主启动类所在包及其子包下的所有带 @Component、@Controller、@Service、@Repository 注解的类,把它们注册成 Spring Bean 。
这就是为啥之前强调"Controller 要和主启动类在同一个包下"------如果不在,@ComponentScan 扫不到,Controller 就不会被注册,访问接口就会 404!
如果想扫描其他包,可手动指定 basePackages:
go
1// 扫描 com.xxx.controller 和 com.xxx.service 包
2@SpringBootApplication(scanBasePackages ={"com.xxx.controller","com.xxx.service"})
3publicclassFirstSpringbootApiApplication{
4// ...
5}
注解3:@EnableAutoConfiguration------SpringBoot "自动配置"的核心
这是最关键的注解,也是 SpringBoot 区别于传统 Spring 的核心!
它的作用:根据项目里的依赖,自动配置对应的组件 。
比如:
-
你加了
spring-boot-starter-web依赖 → 自动配置 Tomcat、DispatcherServlet、Spring MVC 核心组件; -
你加了
spring-boot-starter-data-redis依赖 → 自动配置 RedisTemplate、JedisConnectionFactory; -
你没加某个依赖 → 对应的自动配置就不会生效,避免无用配置。
底层逻辑:SpringBoot 内置了一个"自动配置清单"(spring-boot-autoconfigure 包下的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件),里面列了几百个自动配置类,@EnableAutoConfiguration 会根据你的依赖,按需加载这些配置类。
SpringApplication.run() 到底干了啥?
主启动类的 main 方法里,就一行核心代码:SpringApplication.run(FirstSpringbootApiApplication.class, args),这行代码执行时,会完成整个 SpringBoot 应用的启动流程,咱们简化成 5 个关键步骤:
-
初始化 SpringApplication 对象
:加载应用类型(Web/非 Web)、初始化监听器、初始化应用上下文;
-
启动 Spring 容器(ApplicationContext)
:创建 Bean 工厂、扫描并注册 Bean、执行自动配置;
-
启动内置服务器(Tomcat/Jetty)
:如果是 Web 应用,自动启动内置服务器,绑定 8080 端口;
-
发布应用启动事件
:通知所有监听器"应用启动完成";
-
阻塞主线程
:让应用一直运行(否则 main 方法执行完就退出了)。
你可以在启动日志里看到这些步骤的痕迹:
go
// 初始化 SpringApplication
2026-03-19 10:00:00.000 INFO 12345 --- [ main] c.x.FirstSpringbootApiApplication : Starting FirstSpringbootApiApplication using Java 17.0.10
// 自动配置报告
2026-03-19 10:00:01.234 INFO 12345 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
// 启动 Tomcat
2026-03-19 10:00:01.456 INFO 12345 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http)
// 应用启动完成
2026-03-19 10:00:01.789 INFO 12345 --- [ main] c.x.FirstSpringbootApiApplication : Started FirstSpringbootApiApplication in 2.345 seconds (process running for 3.456)
必避的 3 个坑
-
主启动类包层级太高/太低
:
比如主启动类在
com.xxx,而 Controller 在com.xxx.controller→ 正常;如果 Controller 在
com.yyy→@ComponentScan扫不到,404!解决:要么调整包结构,要么用
scanBasePackages指定扫描包。 -
启动时报"自动配置冲突"
:
比如同时加了 Spring MVC 和 Spring WebFlux 依赖,自动配置不知道该选哪个;
解决:用
@SpringBootApplication(exclude = {某个自动配置类.class})排除冲突的配置。 -
main 方法里写错参数
:
比如把
SpringApplication.run(FirstSpringbootApiApplication.class, args)写成SpringApplication.run(HelloController.class, args)→ 扫描范围错误,启动失败;解决:第一个参数必须是主启动类的 Class 对象。
最后说两句
其实主启动类的核心就两件事:
-
靠
@SpringBootApplication注解完成"配置类标识 + 组件扫描 + 自动配置"; -
靠
SpringApplication.run()方法完成"容器启动 + 服务器启动 + 应用运行"。
理解了这些,你就不会再对"为啥点一下就能跑"感到困惑,遇到启动失败、Bean 找不到、接口 404 等问题,也能精准定位原因。
下一期,咱们会讲:如何定制 SpringBoot 的启动流程(比如加自定义启动 Banner、添加启动监听器),让你的 SpringBoot 应用更"个性化"。
如果这篇文章帮你搞懂了主启动类的底层逻辑,麻烦点个赞、在看,关注我,后续还有更多 SpringBoot 底层干货,从入门到精通,一步步带你吃透~