Q:普通main方法执行完之后程序就停止了 但是启动springboot项目为什么就没有停止
A:
核心原因:Spring Boot创建了非守护线程
1. main方法执行后发生了什么
java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
// 这里SpringApplication.run()会创建非守护线程
}
}
2. SpringApplication.run()的工作原理
java
// Spring Boot源码简化示意
public ConfigurableApplicationContext run(String... args) {
// 1. 准备环境
// 2. 创建应用上下文
// 3. 刷新上下文(关键步骤)
refreshContext(context);
// 4. 执行Runner
// 5. 返回上下文(但此时main线程已完成,程序继续运行)
}
3. 核心机制:嵌入式Web服务器创建非守护线程
Spring Boot启动时会根据依赖创建相应的Web服务器:
java
// Tomcat为例
public class TomcatWebServer {
public void start() throws WebServerException {
// Tomcat会启动线程池,这些线程都是非守护线程
this.tomcat.start();
// 启动一个非守护线程来保持服务器运行
startDaemonAwaitThread();
}
private void startDaemonAwaitThread() {
Thread awaitThread = new Thread("container-" + (containerCounter.get())) {
@Override
public void run() {
// 这个线程会一直阻塞,直到服务器关闭
TomcatWebServer.this.tomcat.getServer().await();
}
};
awaitThread.setDaemon(false); // 非守护线程!
awaitThread.start();
}
}
4. Java线程的守护/非守护机制
java
public class ThreadExample {
public static void main(String[] args) {
// 守护线程:主线程结束后会自动结束
Thread daemonThread = new Thread(() -> {
while (true) {
System.out.println("Daemon running...");
}
});
daemonThread.setDaemon(true);
daemonThread.start();
// 程序会立即结束,因为只有守护线程
}
}
5. Spring Boot保持运行的详细流程
text
Spring Boot启动流程:
1. main线程调用 SpringApplication.run()
2. 创建Spring容器和应用上下文
3. 根据类路径检测到Web依赖(如spring-boot-starter-web)
4. 启动嵌入式Web服务器(Tomcat/Netty/Jetty)
5. Web服务器创建并启动非守护线程:
- 监听端口(如8080)
- 处理HTTP请求
- 保持连接
6. main线程执行完毕,但JVM检测到还有非守护线程在运行
7. JVM保持运行,直到所有非守护线程结束
6. 验证:查看Spring Boot的线程
java
// 可以在Spring Boot启动后查看线程
@SpringBootApplication
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
// 查看所有线程
Thread.getAllStackTraces().keySet().forEach(thread -> {
System.out.println(thread.getName() + " - 是否守护线程: " + thread.isDaemon());
});
}
}
输出示例:
main - 是否守护线程: false
Tomcat-startStop-1 - 是否守护线程: false
http-nio-8080-Acceptor - 是否守护线程: false
http-nio-8080-ClientPoller - 是否守护线程: false
...
7. 如何优雅关闭Spring Boot应用
java
// 方式1:调用close()方法
context.close();
// 方式2:发送关闭信号(如Ctrl+C)
// Spring Boot会注册JVM关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
context.close();
}));
// 方式3:通过Actuator端点
// POST /actuator/shutdown
总结
Spring Boot项目在main方法执行完后不停止的原因是:
- Web服务器创建了非守护线程来监听端口和处理请求
- JVM规则:只要还有非守护线程在运行,JVM就不会退出
- 事件驱动模型:Web服务器在等待HTTP请求,处于阻塞状态
- 优雅的生命周期管理:需要显式关闭或接收关闭信号才会停止
这确保了Spring Boot应用可以作为独立的服务器进程长期运行。