一、普通java程序vs springboot项目的本质
| 维度 | 普通 Java 程序(如 java HelloWorld) |
Spring Boot 服务 |
|---|---|---|
| 运行模式 | 执行完 main() 就结束 |
main() 启动后阻塞等待,不结束 |
| 生命周期 | 秒级/分钟级 | 天级/月级(7×24 运行) |
| 与 JVM 关系 | JVM 随程序结束而退出 | JVM 长期存活,请求在已运行的 JVM 内处理 |
| 类加载时机 | 启动时一次性加载 | 启动时加载核心类,运行时可能懒加载 |
| JIT 编译时机 | 运行期间触发 | 运行期间持续触发,服务越久性能越好 |
二、Spring Boot 为什么能"一直运行不结束"?
核心:非守护线程阻塞
java
// SpringApplication.run() 的核心逻辑(简化)
public ConfigurableApplicationContext run(String... args) {
// 1. 创建 Spring 上下文
ConfigurableApplicationContext context = createApplicationContext();
// 2. 刷新上下文(加载 Bean、启动内嵌 Tomcat)
refreshContext(context);
// 3. 发布启动完成事件
listeners.started(context);
// 4. 调用 Runner(CommandLineRunner/ApplicationRunner)
callRunners(context, args);
// 5. 返回上下文,但 main 线程在这里做了什么?
return context;
}
// 这里需要再去学习下springboot的启动过程
关键点:内嵌 Tomcat/Jetty/Undertow 启动了非守护线程。
java
// Tomcat 启动时会创建线程池处理请求
// 这些线程默认是 非守护线程(non-daemon)
Executor executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new CustomThreadFactory() // 默认创建的是非守护线程
);
JVM 退出条件:所有非守护线程结束。
java
main 线程启动
│
▼
启动 Tomcat(创建非守护线程:Acceptor、Poller、Worker 线程)
│
▼
main 线程执行完 run() 方法
│
▼
main 线程结束,但 JVM 不退出
│
▼
Tomcat 的 Acceptor 线程仍在监听端口(如 8080)
│
▼
有新请求 → Worker 线程从线程池取出处理
│
▼
请求处理完 → Worker 线程归还线程池,**不销毁 JVM**
只要还有非守护线程在运行,JVM 就不会退出。Spring Boot 通过内嵌 Web 服务器维持这些线程。